To Do:

  • dont’ force 2 axes on simulated data? (sometimes models fail though. Would need to use a “safe” version of opls())

Overview

After discussion with Elizabeth we decided to create the following combination of datasets:

  1. Without signal (no covariance) and without discriminating variables. We would predict poor PCA and no separation by PLS-DA
  2. With signal (covariance) and without discriminating variables. We would expect good PCA (high % var explained), but no separation in PCA or PLS-DA
  3. Without signal (covariance) and with discriminating variables. We would expect poor PCA, but significant PLS-DA
  4. With signal and discriminating variables. We would expect good PCA, but little separation in PCA space if only a few discriminating variables, but significant PLS-DA.

I am consider making a Shiny app that allows you to tweak the parameters that generate the data and plots PCA and PLS-DA side-by-side.

Permutation testing: What about using each of these 4 parameter combinations to create ~100 randomly generated datasets. Then with each, I could do PCA followed by t-tests on the PC axes vs. PLS-DA to demonstrate which is better at detecting real separation in the data. Maybe the cherry-picked figure is enough, but this would only be a few sentences in the manuscript and would be valuable I think.

To-Do: Add MANNOVA or PERMANOVA to this

Setup

Load Packages

# Required Packages
library(MASS)
library(tidyverse)
library(ropls)
library(chemhelper)
#chemhelper contains the sim_multvar() function I wrote as well as custom functions for interfacing with ropls package in a friendlier way.  
#Install with devtools::install_github("Aariq/chemhelper")
library(cowplot) #for making and saving prettier plots
library(broom) #for tidy(), which turns model output into dataframes
library(vegan) #for PERMANOVA
library(iheatmapr) #for correlation heatmaps

Create Custom Plotting Functions

# Functions for plotting
library(latex2exp)
pca_plot <- function(pca.opls, group_var){
  plotdata <- chemhelper::get_plotdata(pca.opls)
  ggplot(plotdata$plot_data, aes(x = p1, y = p2, color = group_var)) +
    geom_point() +
    stat_ellipse() +
    labs(x = paste0("PC1 (", plotdata$var_explained$R2X[1] * 100, "%)"),
         y = paste0("PC2 (", plotdata$var_explained$R2X[2] * 100, "%)")) +
    scale_colour_discrete("Group Membership") +
    theme_bw() +
    ggtitle("PCA") +
    labs(caption = TeX(
      paste0(nrow(plotdata$var_explained), " principal components;",
             "$R^2(cumulative) = ", max(plotdata$var_explained$`R2X(cum)`, "$"))))
}
plsda_plot <- function(plsda.opls){
  plotdata <- chemhelper::get_plotdata(plsda.opls)
  ggplot(plotdata$plot_data, aes(x = p1, y = p2, color = y1)) +
    geom_point() +
    stat_ellipse() +
    labs(x = paste0("P1 (", plotdata$axis_stats$R2X[1] * 100, "%)"),
         y = paste0("P2 (", plotdata$axis_stats$R2X[2] * 100, "%)")) +
    scale_color_discrete("Group Membership") +
    theme_bw() +
    ggtitle("PLS-DA") +
    labs(caption = TeX(
      paste0("$R^{2}_{Y} = ", plotdata$model_stats$`R2Y(cum)`, "$; ",
             "$Q^{2} = ", plotdata$model_stats$`Q2(cum)`, "$; ",
             "$p_{Q^{2}} = ", plotdata$model_stats$pQ2, "$")))
}
oplsda_plot <- function(oplsda.opls){
  plotdata <- chemhelper::get_plotdata(oplsda.opls)
  ggplot(plotdata$plot_data, aes(x = p1, y = o1, color = y1)) +
    geom_point() +
    stat_ellipse() +
    labs(x = paste0("Pred (", plotdata$axis_stats$R2X[1] * 100, "%)"),
         y = paste0("Ortho (", plotdata$axis_stats$R2X[2] * 100, "%)")) +
    scale_color_discrete("Group Membership") +
    theme_bw() +
    ggtitle("OPLS-DA") +
    labs(caption = TeX(
      paste0("$R^{2}_{Y}=", plotdata$model_stats$`R2Y(cum)`, "$; ",
             "$Q^{2}=", plotdata$model_stats$`Q2(cum)`, "$; ",
             "$p_{Q^{2}}=", plotdata$model_stats$pQ2, "$")))
}

Create Custom Summary Table Function

my_table <- function(data, pca, plsda){
  VIPs <- get_VIP(plsda)
  
  loadings_pls <- getLoadingMN(plsda) %>%
    as.data.frame() %>%
    rownames_to_column(var = "Variable") %>% 
    rename(p1_loading = p1, p2_loading = p2)
  
  loadings_pca <- getLoadingMN(pca) %>%
    as.data.frame() %>% 
    rownames_to_column(var = "Variable") %>% 
    select(Variable, PC1_loading = p1, PC2_loading = p2)
  
  t_tests <- data %>%
    select(-group) %>%
    map_df(~t.test(.~data$group)$p.value) %>%
    gather(key = Variable, value = t_test_p.value)
  
  join1 <- full_join(VIPs, loadings_pls)
  join2 <- full_join(join1, loadings_pca)
  join3 <- full_join(join2, t_tests)
  return(join3)
}

Create safe version of opls()

Occasionally I was getting ‘matrix is singular’ type errors from opls(), which I think is just a chance occurrance with the random data. This “safe” version will always return NULL when there is any kind of error.

safe.opls <- possibly(opls, otherwise = NULL)

Set global options (needle in haystack scenario)

I’ll set a few global options here to make it easier to play around with the simulation.

These options basically create a situation where when there are real differences between groups, it’s only due to a small percentage of variables. Other variables can have mild covariation or none at all.

N = 30 #total number of observations
P = 40 #total number of variables
signal.prop = 0.625 #when there are correlated variables, what proportion?
cov_corvar = 0.8 #covariance for signal variables
disc.prop = 0.125 #when there are discriminating variables, what proportion?
diff_discr = 1.2 #mean difference between groups for discriminating variables
nperm = 50 #how many perumutations
seed = 100 #seed
#calculated values
(p_corvar = round(P*signal.prop))
[1] 25
(p_discr = round(P*disc.prop))
[1] 5

1. No Covariance, No Discrimination

Generate data

sim.data1 <- sim_multvar(p_uncorvar = P,
                      p_corvar = 0,
                      p_discr = 0,
                      cov_corvar = 0,
                      diff_discr = 0,
                      N = N,
                      seed = seed)
sim.data1 %>% select(-group) %>% as.matrix() %>% cor() %>% iheatmap(row_labels = TRUE, col_labels = TRUE)

PCA & PLS-DA

Run PCA

sim.pca1 <- opls(select(sim.data1, -group), plotL = FALSE)
PCA
30 samples x 40 variables
standard scaling of predictors
      R2X(cum) pre ort
Total    0.555   7   0

Run PLS-DA

sim.plsda1 <- try(opls(select(sim.data1, -group), sim.data1$group,
                  plotL = FALSE))
Error : No model was built because the first predictive component was already not significant;
Select a number of predictive components of 1 if you want the algorithm to compute a model despite this.

This fails, but I’ll force two axes for the sake of plotting.

## Must force axes
sim.plsda1 <- opls(select(sim.data1, -group), sim.data1$group,
                   predI = 2,
                   permI = 200,
                   plotL = FALSE)
PLS-DA
30 samples x 40 variables and 1 response
standard scaling of predictors and response(s)
      R2X(cum) R2Y(cum) Q2(cum) RMSEE pre ort pR2Y  pQ2
Total    0.148    0.751  -0.765 0.263   2   0 0.64 0.36

Plots:

p1 <- plot_grid(pca_plot(sim.pca1, sim.data1$group) +
                 theme(legend.position = "none") +
                 ggtitle("PCA", subtitle = "No covariance, no discriminating variables"),
               plsda_plot(sim.plsda1) +
                 theme(legend.position = "none") +
                 ggtitle("PLS-DA", subtitle = "No covariance, no discriminating variables"))
p1

Get PC axis loadings and VIP scores.

Note: It’s probably not very responsible to look at VIP scores from a PLS-DA model that is not significant

my_table(sim.data1, sim.pca1, sim.plsda1) %>% arrange(desc(VIP))
Joining, by = "Variable"
Joining, by = "Variable"
Joining, by = "Variable"

Permutation testing

First, make a bunch of datasets with the same parameters

sim.data1.list <- map(1:nperm,
    ~sim_multvar(p_uncorvar = P,
              p_corvar = 0,
              p_discr = 0,
              cov_corvar = 0,
              diff_discr = 0,
              N = N,
              seed = NA))
names(sim.data1.list) <- 1:nperm

Now, do PCA on all of them and get scores

Now do t-tests on all of them along PCs 1 and 2 and get p-values

#The 'group' variable is the same in all datasets.
grouping <- sim.data1.list[[1]]$group
p1.testresults <- scores.data1.list %>%
  #maps t.test() function to all dataframes and converts output into dataframe with tidy()
  map_dfr(~t.test(.$p1 ~ grouping) %>% tidy(), .id = "dataset") %>% 
  #selects just columns of interest
  select(dataset,
         PC1.effect.size = "estimate",
         PC1.t = "statistic",
         PC1.p.value = "p.value")
p2.testresults <- scores.data1.list %>%
  map_dfr(~t.test(.$p2 ~ grouping) %>% tidy(), .id = "dataset") %>%
  select(dataset,
         PC2.effect.size = "estimate",
         PC2.t = "statistic",
         PC2.p.value = "p.value")
data1.PCAresults <- bind_cols(p1.testresults, p2.testresults)

And do PERMANOVA on all of them

data1.permanova <- map_dbl(sim.data1.list,
    ~ adonis(select(., -group) ~ grouping, method = "eu")$aov.tab$`Pr(>F)`[1])

Now do PLS-DA on all of them and get p-values (this will take a long time)

data1.PLSresults <- pls.data1.list %>%
  map_dfr(~get_plotdata(.)$model_stats, .id = "dataset")
data1.comparison <- full_join(data1.PCAresults, data1.PLSresults) %>% add_column("permanova" = data1.permanova)
p1.perm <- ggplot(data1.comparison) +
  geom_density(aes(PC1.p.value), fill = "blue", alpha = 0.33) +
  geom_density(aes(pQ2), fill = "red", alpha = 0.33) +
  geom_density(aes(permanova), fill = "green", alpha = 0.33) +
  labs(x = "p value") +
  geom_vline(xintercept = 0.05, linetype = 5) +
  ggtitle("1. -Covariance, -Discriminating variables")
p1.perm

2. Yes Covariance, No Discrimination

Generate Data:

PCA & PLS-DA

Run PCA

sim.pca2 <- opls(select(sim.data2, -group), plotL = FALSE)
PCA
30 samples x 40 variables
standard scaling of predictors
      R2X(cum) pre ort
Total    0.523   3   0

Run PLS-DA

sim.plsda2 <- try(opls(select(sim.data2, -group), sim.data2$group,
                  plotL = FALSE))
Error : No model was built because the first predictive component was already not significant;
Select a number of predictive components of 1 if you want the algorithm to compute a model despite this.

This fails, but I’ll force two axes for the sake of plotting.

## Must force axes
sim.plsda2 <- opls(select(sim.data2, -group), sim.data2$group,
                   predI = 2,
                   permI = 200, plotL = FALSE)
PLS-DA
30 samples x 40 variables and 1 response
standard scaling of predictors and response(s)
      R2X(cum) R2Y(cum) Q2(cum) RMSEE pre ort pR2Y pQ2
Total    0.247    0.317   -3.43 0.436   2   0    1   1

Plots:

p2 <- plot_grid(pca_plot(sim.pca2, sim.data2$group) +
                 theme(legend.position = "none") +
                 ggtitle("PCA", subtitle = "Yes covariance, no discriminating variables"),
               plsda_plot(sim.plsda2) +
                 theme(legend.position = "none") +
                 ggtitle("PLS-DA", subtitle = "Yes covariance, no discriminating variables"))
p2

PC1 slightly better, but overall R^2 the same. PLS-DA still not significant

Get PC axis loadings and VIP scores.

Note: It’s probably not responsible to look at VIP scores from a PLS-DA model that is not significant

my_table(sim.data2, sim.pca2, sim.plsda2) %>% arrange(desc(VIP))
Joining, by = "Variable"
Joining, by = "Variable"
Joining, by = "Variable"

Permutation testing

First, make a bunch of datasets with the same parameters

sim.data2.list <- map(1:nperm,
                      ~sim_multvar(p_uncorvar = P - p_corvar,
                                   p_corvar = p_corvar,
                                   p_discr = 0,
                                   cov_corvar = cov_corvar,
                                   diff_discr = 0,
                                   N = N,
                                   seed = NA))
names(sim.data2.list) <- 1:nperm

Now, do PCA on all of them and get loadings.

Now do t-tests on all of them along PCs 1 and 2 and get p-values

#The 'group' variable is the same in all datasets.
grouping <- sim.data2.list[[1]]$group
p1.testresults <- scores.data2.list %>%
  #maps t.test() function to all dataframes and converts output into dataframe with tidy()
  map_dfr(~t.test(.$p1~grouping) %>% tidy(), .id = "dataset") %>% 
  #selects just columns of interest
  select(dataset,
         PC1.effect.size = "estimate",
         PC1.t = "statistic",
         PC1.p.value = "p.value")
p2.testresults <- scores.data2.list %>%
  map_dfr(~t.test(.$p2~grouping) %>% tidy(), .id = "dataset") %>%
  select(dataset,
         PC2.effect.size = "estimate",
         PC2.t = "statistic",
         PC2.p.value = "p.value")
Error in model.frame.default(formula = .$p2 ~ grouping) : 
  invalid type (NULL) for variable '.$p2'

And do PERMANOVA on all of them

data2.permanova <- map_dbl(sim.data2.list,
    ~ adonis(select(., -group) ~ grouping, method = "eu")$aov.tab$`Pr(>F)`[1])

Now do PLS-DA on all of them and get p-values (this will take a long time)

data2.PLSresults <- pls.data2.list %>% map_dfr(~get_plotdata(.)$model_stats, .id = "dataset")
data2.PLSresults
data2.comparison <- full_join(data2.PCAresults, data2.PLSresults) %>%
  add_column("permanova" = data2.permanova)
p2.perm <- ggplot(data2.comparison) +
  geom_density(aes(PC1.p.value), fill = "blue", alpha = 0.33) +
  geom_density(aes(pQ2), fill = "red", alpha = 0.33) +
  geom_density(aes(permanova), fill = "green", alpha = 0.33) +
  labs(x = "p value") +
  geom_vline(xintercept = 0.05, linetype = 5) +
  ggtitle("2. + Covariance, - Discriminating variables")
p2.perm

3. No Covariance, Yes Discrimination

Generate Data:

PCA & PLS-DA

Run PCA

sim.pca3 <- opls(select(sim.data3, -group), plotL = FALSE)
PCA
30 samples x 40 variables
standard scaling of predictors
      R2X(cum) pre ort
Total    0.534   6   0

Run PLS-DA

sim.plsda3
PLS-DA
30 samples x 40 variables and 1 response
standard scaling of predictors and response(s)
      R2X(cum) R2Y(cum) Q2(cum) RMSEE pre ort  pR2Y   pQ2
Total    0.133    0.614   0.331 0.322   1   0 0.265 0.015

Single component model only, so force two axes for the sake of plotting:

sim.plsda3 <- opls(select(sim.data3, -group), sim.data3$group,
                   permI = 200,
                   predI = 2,
                   plotL = FALSE)
PLS-DA
30 samples x 40 variables and 1 response
standard scaling of predictors and response(s)
      R2X(cum) R2Y(cum) Q2(cum) RMSEE pre ort pR2Y  pQ2
Total    0.195    0.794 -0.0334 0.239   2   0 0.23 0.06

Plots:

p3 <- plot_grid(pca_plot(sim.pca3, sim.data3$group) +
                 theme(legend.position = "none") +
                 ggtitle("PCA", subtitle = "No covariance, yes discriminating variables"),
               plsda_plot(sim.plsda3) +
                 theme(legend.position = "none") +
                 ggtitle("PLS-DA", subtitle = "No covariance, yes discriminating variables"))
p3

PLS-DA is still not great. Single component model is signifcant. Forced 2 axes for PLS-DA plot. It might be the case that some co-variance between discriminating variables is required for PLS-DA to pull them out. If they are completely orthogonal, how can it draw the “regression line” through 5-dimensional space?

Get PC axis loadings and VIP scores.

my_table(sim.data3, sim.pca3, sim.plsda3) %>% arrange(desc(VIP))
Joining, by = "Variable"
Joining, by = "Variable"
Joining, by = "Variable"

Permutation testing

First, make a bunch of datasets with the same parameters

sim.data3.list <- map(1:nperm,
                      ~sim_multvar(p_uncorvar = P - p_discr,
                                p_corvar = 0,
                                p_discr = p_discr,
                                cov_corvar = 0,
                                diff_discr = diff_discr,
                                N = N,
                                seed = NA))

Now, do PCA on all of them and get loadings.

Now do t-tests on all of them along PCs 1 and 2 and get p-values

#The 'group' variable is the same in all datasets.
grouping <- sim.data3.list[[1]]$group
p1.testresults <- scores.data3.list %>%
  #maps t.test() function to all dataframes and converts output into dataframe with tidy()
  map_dfr(~t.test(.$p1~grouping) %>% tidy(), .id = "dataset") %>% 
  #selects just columns of interest
  select(dataset,
         PC1.effect.size = "estimate",
         PC1.t = "statistic",
         PC1.p.value = "p.value")
p2.testresults <- scores.data3.list %>%
  map_dfr(~t.test(.$p2~grouping) %>% tidy(), .id = "dataset") %>%
  select(dataset,
         PC2.effect.size = "estimate",
         PC2.t = "statistic",
         PC2.p.value = "p.value")
data3.PCAresults <- bind_cols(p1.testresults, p2.testresults)

And do PERMANOVA on all of them

data3.permanova <- map_dbl(sim.data3.list,
    ~ adonis(select(., -group) ~ grouping, method = "eu")$aov.tab$`Pr(>F)`[1])

Now do PLS-DA on all of them and get p-values (this will take a long time)

data3.PLSresults <- pls.data3.list %>% map_dfr(~get_plotdata(.)$model_stats, .id = "dataset")
data3.PLSresults
data3.comparison <- full_join(data3.PCAresults, data3.PLSresults) %>% add_column("permanova" = data3.permanova)
p3.perm <- ggplot(data3.comparison) +
  geom_density(aes(PC1.p.value), fill = "blue", alpha = 0.33) +
  geom_density(aes(pQ2), fill = "red", alpha = 0.33) +
  geom_density(aes(permanova), fill = "green", alpha = 0.33) +
  labs(x = "p value") +
  geom_vline(xintercept = 0.05, linetype = 5) +
  ggtitle("3. - Covariance, + Discriminating variables")
p3.perm

4. Yes Covariance, Yes Discrimination

Generate Data:

PCA & PLS-DA

Run PCA

sim.pca4 <- opls(select(sim.data4, -group), plotL = FALSE)
PCA
30 samples x 40 variables
standard scaling of predictors
      R2X(cum) pre ort
Total    0.565   3   0

Run PLS-DA

sim.plsda4 <- opls(select(sim.data4, -group), sim.data4$group,
                   permI = 200,
                   plotL = FALSE)
PLS-DA
30 samples x 40 variables and 1 response
standard scaling of predictors and response(s)
      R2X(cum) R2Y(cum) Q2(cum) RMSEE pre ort  pR2Y  pQ2
Total    0.115    0.518   0.159 0.359   1   0 0.015 0.05

This produces a 1-component model. I’ll force two axes for the sake of plotting:

sim.plsda4 <- opls(select(sim.data4, -group), sim.data4$group,
                   permI = 200,
                   predI = 2,
                   plotL = FALSE)
PLS-DA
30 samples x 40 variables and 1 response
standard scaling of predictors and response(s)
      R2X(cum) R2Y(cum) Q2(cum) RMSEE pre ort  pR2Y   pQ2
Total    0.354    0.604  -0.143 0.332   2   0 0.085 0.335

Plots:

p4 <- plot_grid(pca_plot(sim.pca4, sim.data4$group) +
                 theme(legend.position = "none") +
                 ggtitle("PCA", subtitle = "Yes covariance, Yes discriminating variables"),
               plsda_plot(sim.plsda4) +
                 theme(legend.position = "none") +
                 ggtitle("PLS-DA", subtitle = "Yes covariance, Yes discriminating variables"))
p4

Get PC axis loadings and VIP scores.

my_table(sim.data4, sim.pca4, sim.plsda4) %>% arrange(desc(VIP))
Joining, by = "Variable"
Joining, by = "Variable"
Joining, by = "Variable"

Permutation testing

First, make a bunch of datasets with the same parameters

sim.data4.list <- map(1:nperm,
                      ~sim_multvar(p_uncorvar = P - p_corvar - p_discr,
                                   p_corvar = p_corvar,
                                   p_discr = p_discr,
                                   cov_corvar = cov_corvar,
                                   diff_discr = diff_discr,
                                   cov_discr = 0.2,
                                   N = N,
                                   seed = NA)) %>% 
  set_names(1:nperm)

Now, do PCA on all of them and get loadings.

Now do t-tests on all of them along PCs 1 and 2 and get p-values

#The 'group' variable is the same in all datasets.
grouping <- sim.data4.list[[1]]$group
p1.testresults <- scores.data4.list %>%
  #maps t.test() function to all dataframes and converts output into dataframe with tidy()
  map_dfr(~t.test(.$p1~grouping) %>% tidy(), .id = "dataset") %>% 
  #selects just columns of interest
  select(dataset,
         PC1.effect.size = "estimate",
         PC1.t = "statistic",
         PC1.p.value = "p.value")
# p2.testresults <- scores.data4.list %>%
#   map_dfr(~t.test(.$p2~grouping) %>% tidy(), .id = "dataset") %>%
#   select(dataset,
#          PC2.effect.size = "estimate",
#          PC2.t = "statistic",
#          PC2.p.value = "p.value")
# data4.PCAresults <- bind_cols(p1.testresults, p2.testresults)
data4.PCAresults <- p1.testresults

And do PERMANOVA on all of them

data4.permanova <- map_dbl(sim.data4.list,
    ~ adonis(select(., -group) ~ grouping, method = "eu")$aov.tab$`Pr(>F)`[1])

Now do PLS-DA on all of them and get p-values (this will take a long time)

data4.PLSresults <- pls.data4.list %>% map_dfr(~get_plotdata(.)$model_stats, .id = "dataset")
data4.PLSresults
data4.comparison <- full_join(data4.PCAresults, data4.PLSresults) %>% add_column("permanova" = data4.permanova)
p4.perm <- ggplot(data4.comparison) +
  geom_density(aes(PC1.p.value), fill = "blue", alpha = 0.33) +
  geom_density(aes(pQ2), fill = "red", alpha = 0.33) +
  geom_density(aes(permanova), fill = "green", alpha = 0.33) +
  labs(x = "p value") +
  geom_vline(xintercept = 0.05, linetype = 5) +
  ggtitle("4. + Covariance, + Discriminating variables")
p4.perm

Plot example datasets

Plot permutation testing results

Conclusions

None of the methods give more false positives than they should—that is, no differences are found when there are no discriminating variables.

PERMANOVA seems to perform the best in these simulations, followed by PLS-DA. PCA is shit at finding separation when discriminating variables are a small portion of the total variables measured (needle in a haystack scenario).

When covariance is added to the “haystack”, or to both the “haystack” and the “needle”, the performance of PERMANOVA drops, while the performance of PLS-DA remains about the same. I should play around with this in a separate notebook.

Trying alternate method of making discriminating variables

I can’t even figure out how to make PLS-DA find a difference.

sim.data5 <- sim.vcov(p_noise = 0,
                      p_signal = 15,
                      p_disc = 15,
                      cov_signal = 0.8,
                      cov_disc = 0.6,
                      diff_disc = 0,
                      var = 0.8,
                      N = 20,
                      seed = NA)
sim.data5 <- sim.data5 %>%
  rename(Y = disc_1) %>% 
  arrange(Y) %>% 
  mutate(group = c(rep("a", nrow(.)/2), rep("b", nrow(.)/2)))
pca5 <- opls(select(sim.data5, -Y, -group), predI = 2)
PCA
20 samples x 29 variables
standard scaling of predictors
      R2X(cum) pre ort
Total    0.882   2   0

pca_plot(pca5, sim.data5$group)

plsda5 <- opls(select(sim.data5, -group, -Y), sim.data5$group, permI = 200, predI = 2)
PLS-DA
20 samples x 29 variables and 1 response
standard scaling of predictors and response(s)
      R2X(cum) R2Y(cum) Q2(cum) RMSEE pre ort pR2Y   pQ2
Total    0.882    0.507   0.327 0.381   2   0 0.08 0.025

get_VIP(plsda5) %>% arrange(desc(VIP))
pls5 <- opls(select(sim.data5, -group, -Y), sim.data5$Y, permI = 200, predI = 2)
PLS
20 samples x 29 variables and 1 response
standard scaling of predictors and response(s)
      R2X(cum) R2Y(cum) Q2(cum) RMSEE pre ort pR2Y   pQ2
Total     0.88     0.64   0.471 0.498   2   0 0.02 0.005

Huh, that doesn’t work

plotdata <- sim.data5 %>% 
  gather(-group, -Y, key = variable, value = data) %>% 
  mutate(vartype = case_when(str_detect(variable, "disc") ~ "discriminating",
                             str_detect(variable, "noise") ~ "no covariance",
                             str_detect(variable, "signal") ~ "covarying"))
ggplot(plotdata, aes(x = Y, y = data, color = vartype)) +
  geom_point() +
  geom_smooth(method ="lm", se = FALSE)

Ah ha! Is it because the discriminating data actually does NOT co-vary? No, I upped the covariance and it doesn’t help.

Permanova

permdata5 <- sim.data5 %>%
  # select(-Y) %>% 
  mutate_if(is.double, scale)
adonis(select(permdata5, -group, -Y)~permdata5$group*permdata5$Y, method = "eu")

Call:
adonis(formula = select(permdata5, -group, -Y) ~ permdata5$group *      permdata5$Y, method = "eu") 

Permutation: free
Number of permutations: 999

Terms added sequentially (first to last)

                            Df SumsOfSqs MeanSqs F.Model      R2 Pr(>F)  
permdata5$group              1     93.01  93.015  3.8219 0.16881  0.027 *
permdata5$Y                  1     54.00  53.997  2.2187 0.09800  0.104  
permdata5$group:permdata5$Y  1     14.59  14.587  0.5994 0.02647  0.546  
Residuals                   16    389.40  24.338         0.70672         
Total                       19    551.00                 1.00000         
---
Signif. codes:  0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1
LS0tCnRpdGxlOiAiU2ltdWxhdGVkIERhdGEgQW5hbHl6ZWQgd2l0aCBQQ0EgYW5kIFBMUy1EQSIKYXV0aG9yOiBFcmljIFIgU2NvdHQKb3V0cHV0OiAKICBodG1sX25vdGVib29rOiAKICAgIGNvZGVfZm9sZGluZzogaGlkZQogICAgaGlnaGxpZ2h0OiBoYWRkb2NrCiAgICB0aGVtZTogZmxhdGx5CiAgICB0b2M6IHllcwogICAgdG9jX2Zsb2F0OiB5ZXMKLS0tCiMgVG8gRG86Ci0gZG9udCcgZm9yY2UgMiBheGVzIG9uIHNpbXVsYXRlZCBkYXRhPyAoc29tZXRpbWVzIG1vZGVscyBmYWlsIHRob3VnaC4gIFdvdWxkIG5lZWQgdG8gdXNlIGEgInNhZmUiIHZlcnNpb24gb2YgYG9wbHMoKWApCgoKIyBPdmVydmlldwpBZnRlciBkaXNjdXNzaW9uIHdpdGggRWxpemFiZXRoIHdlIGRlY2lkZWQgdG8gY3JlYXRlIHRoZSBmb2xsb3dpbmcgY29tYmluYXRpb24gb2YgZGF0YXNldHM6CgoxLiBXaXRob3V0IHNpZ25hbCAobm8gY292YXJpYW5jZSkgYW5kIHdpdGhvdXQgZGlzY3JpbWluYXRpbmcgdmFyaWFibGVzLiAgV2Ugd291bGQgcHJlZGljdCBwb29yIFBDQSBhbmQgbm8gc2VwYXJhdGlvbiBieSBQTFMtREEKMi4gV2l0aCBzaWduYWwgKGNvdmFyaWFuY2UpIGFuZCB3aXRob3V0IGRpc2NyaW1pbmF0aW5nIHZhcmlhYmxlcy4gIFdlIHdvdWxkIGV4cGVjdCBnb29kIFBDQSAoaGlnaCAlIHZhciBleHBsYWluZWQpLCBidXQgbm8gc2VwYXJhdGlvbiBpbiBQQ0Egb3IgUExTLURBCjMuIFdpdGhvdXQgc2lnbmFsIChjb3ZhcmlhbmNlKSBhbmQgKip3aXRoKiogZGlzY3JpbWluYXRpbmcgdmFyaWFibGVzLiBXZSB3b3VsZCBleHBlY3QgcG9vciBQQ0EsIGJ1dCBzaWduaWZpY2FudCBQTFMtREEKNC4gV2l0aCBzaWduYWwgKiphbmQqKiBkaXNjcmltaW5hdGluZyB2YXJpYWJsZXMuICBXZSB3b3VsZCBleHBlY3QgZ29vZCBQQ0EsIGJ1dCBsaXR0bGUgc2VwYXJhdGlvbiBpbiBQQ0Egc3BhY2UgaWYgb25seSBhIGZldyBkaXNjcmltaW5hdGluZyB2YXJpYWJsZXMsIGJ1dCBzaWduaWZpY2FudCBQTFMtREEuCgpJIGFtIGNvbnNpZGVyIG1ha2luZyBhIFNoaW55IGFwcCB0aGF0IGFsbG93cyB5b3UgdG8gdHdlYWsgdGhlIHBhcmFtZXRlcnMgdGhhdCBnZW5lcmF0ZSB0aGUgZGF0YSBhbmQgcGxvdHMgUENBIGFuZCBQTFMtREEgc2lkZS1ieS1zaWRlLgoKKlBlcm11dGF0aW9uIHRlc3Rpbmc6KgpXaGF0IGFib3V0IHVzaW5nIGVhY2ggb2YgdGhlc2UgNCBwYXJhbWV0ZXIgY29tYmluYXRpb25zIHRvIGNyZWF0ZSB+MTAwIHJhbmRvbWx5IGdlbmVyYXRlZCBkYXRhc2V0cy4gIFRoZW4gd2l0aCBlYWNoLCBJIGNvdWxkIGRvIFBDQSBmb2xsb3dlZCBieSB0LXRlc3RzIG9uIHRoZSBQQyBheGVzIHZzLiBQTFMtREEgdG8gZGVtb25zdHJhdGUgd2hpY2ggaXMgYmV0dGVyIGF0IGRldGVjdGluZyByZWFsIHNlcGFyYXRpb24gaW4gdGhlIGRhdGEuICBNYXliZSB0aGUgY2hlcnJ5LXBpY2tlZCBmaWd1cmUgaXMgZW5vdWdoLCBidXQgdGhpcyB3b3VsZCBvbmx5IGJlIGEgZmV3IHNlbnRlbmNlcyBpbiB0aGUgbWFudXNjcmlwdCBhbmQgd291bGQgYmUgdmFsdWFibGUgSSB0aGluay4KClRvLURvOiBBZGQgTUFOTk9WQSBvciBQRVJNQU5PVkEgdG8gdGhpcwoKIyBTZXR1cAojIyBMb2FkIFBhY2thZ2VzCmBgYHtyIG1lc3NhZ2U9RkFMU0UsIHdhcm5pbmc9RkFMU0V9CiMgUmVxdWlyZWQgUGFja2FnZXMKbGlicmFyeShNQVNTKQpsaWJyYXJ5KHRpZHl2ZXJzZSkKbGlicmFyeShyb3BscykKbGlicmFyeShjaGVtaGVscGVyKQojY2hlbWhlbHBlciBjb250YWlucyB0aGUgc2ltX211bHR2YXIoKSBmdW5jdGlvbiBJIHdyb3RlIGFzIHdlbGwgYXMgY3VzdG9tIGZ1bmN0aW9ucyBmb3IgaW50ZXJmYWNpbmcgd2l0aCByb3BscyBwYWNrYWdlIGluIGEgZnJpZW5kbGllciB3YXkuICAKI0luc3RhbGwgd2l0aCBkZXZ0b29sczo6aW5zdGFsbF9naXRodWIoIkFhcmlxL2NoZW1oZWxwZXIiKQpsaWJyYXJ5KGNvd3Bsb3QpICNmb3IgbWFraW5nIGFuZCBzYXZpbmcgcHJldHRpZXIgcGxvdHMKbGlicmFyeShicm9vbSkgI2ZvciB0aWR5KCksIHdoaWNoIHR1cm5zIG1vZGVsIG91dHB1dCBpbnRvIGRhdGFmcmFtZXMKbGlicmFyeSh2ZWdhbikgI2ZvciBQRVJNQU5PVkEKbGlicmFyeShpaGVhdG1hcHIpICNmb3IgY29ycmVsYXRpb24gaGVhdG1hcHMKYGBgCgojIyBDcmVhdGUgQ3VzdG9tIFBsb3R0aW5nIEZ1bmN0aW9ucwpgYGB7cn0KIyBGdW5jdGlvbnMgZm9yIHBsb3R0aW5nCmxpYnJhcnkobGF0ZXgyZXhwKQpwY2FfcGxvdCA8LSBmdW5jdGlvbihwY2Eub3BscywgZ3JvdXBfdmFyKXsKICBwbG90ZGF0YSA8LSBjaGVtaGVscGVyOjpnZXRfcGxvdGRhdGEocGNhLm9wbHMpCiAgZ2dwbG90KHBsb3RkYXRhJHBsb3RfZGF0YSwgYWVzKHggPSBwMSwgeSA9IHAyLCBjb2xvciA9IGdyb3VwX3ZhcikpICsKICAgIGdlb21fcG9pbnQoKSArCiAgICBzdGF0X2VsbGlwc2UoKSArCiAgICBsYWJzKHggPSBwYXN0ZTAoIlBDMSAoIiwgcGxvdGRhdGEkdmFyX2V4cGxhaW5lZCRSMlhbMV0gKiAxMDAsICIlKSIpLAogICAgICAgICB5ID0gcGFzdGUwKCJQQzIgKCIsIHBsb3RkYXRhJHZhcl9leHBsYWluZWQkUjJYWzJdICogMTAwLCAiJSkiKSkgKwogICAgc2NhbGVfY29sb3VyX2Rpc2NyZXRlKCJHcm91cCBNZW1iZXJzaGlwIikgKwogICAgdGhlbWVfYncoKSArCiAgICBnZ3RpdGxlKCJQQ0EiKSArCiAgICBsYWJzKGNhcHRpb24gPSBUZVgoCiAgICAgIHBhc3RlMChucm93KHBsb3RkYXRhJHZhcl9leHBsYWluZWQpLCAiIHByaW5jaXBhbCBjb21wb25lbnRzOyIsCiAgICAgICAgICAgICAiJFJeMihjdW11bGF0aXZlKSA9ICIsIG1heChwbG90ZGF0YSR2YXJfZXhwbGFpbmVkJGBSMlgoY3VtKWAsICIkIikpKSkKfQoKcGxzZGFfcGxvdCA8LSBmdW5jdGlvbihwbHNkYS5vcGxzKXsKICBwbG90ZGF0YSA8LSBjaGVtaGVscGVyOjpnZXRfcGxvdGRhdGEocGxzZGEub3BscykKICBnZ3Bsb3QocGxvdGRhdGEkcGxvdF9kYXRhLCBhZXMoeCA9IHAxLCB5ID0gcDIsIGNvbG9yID0geTEpKSArCiAgICBnZW9tX3BvaW50KCkgKwogICAgc3RhdF9lbGxpcHNlKCkgKwogICAgbGFicyh4ID0gcGFzdGUwKCJQMSAoIiwgcGxvdGRhdGEkYXhpc19zdGF0cyRSMlhbMV0gKiAxMDAsICIlKSIpLAogICAgICAgICB5ID0gcGFzdGUwKCJQMiAoIiwgcGxvdGRhdGEkYXhpc19zdGF0cyRSMlhbMl0gKiAxMDAsICIlKSIpKSArCiAgICBzY2FsZV9jb2xvcl9kaXNjcmV0ZSgiR3JvdXAgTWVtYmVyc2hpcCIpICsKICAgIHRoZW1lX2J3KCkgKwogICAgZ2d0aXRsZSgiUExTLURBIikgKwogICAgbGFicyhjYXB0aW9uID0gVGVYKAogICAgICBwYXN0ZTAoIiRSXnsyfV97WX0gPSAiLCBwbG90ZGF0YSRtb2RlbF9zdGF0cyRgUjJZKGN1bSlgLCAiJDsgIiwKICAgICAgICAgICAgICIkUV57Mn0gPSAiLCBwbG90ZGF0YSRtb2RlbF9zdGF0cyRgUTIoY3VtKWAsICIkOyAiLAogICAgICAgICAgICAgIiRwX3tRXnsyfX0gPSAiLCBwbG90ZGF0YSRtb2RlbF9zdGF0cyRwUTIsICIkIikpKQp9CgpvcGxzZGFfcGxvdCA8LSBmdW5jdGlvbihvcGxzZGEub3Bscyl7CiAgcGxvdGRhdGEgPC0gY2hlbWhlbHBlcjo6Z2V0X3Bsb3RkYXRhKG9wbHNkYS5vcGxzKQogIGdncGxvdChwbG90ZGF0YSRwbG90X2RhdGEsIGFlcyh4ID0gcDEsIHkgPSBvMSwgY29sb3IgPSB5MSkpICsKICAgIGdlb21fcG9pbnQoKSArCiAgICBzdGF0X2VsbGlwc2UoKSArCiAgICBsYWJzKHggPSBwYXN0ZTAoIlByZWQgKCIsIHBsb3RkYXRhJGF4aXNfc3RhdHMkUjJYWzFdICogMTAwLCAiJSkiKSwKICAgICAgICAgeSA9IHBhc3RlMCgiT3J0aG8gKCIsIHBsb3RkYXRhJGF4aXNfc3RhdHMkUjJYWzJdICogMTAwLCAiJSkiKSkgKwogICAgc2NhbGVfY29sb3JfZGlzY3JldGUoIkdyb3VwIE1lbWJlcnNoaXAiKSArCiAgICB0aGVtZV9idygpICsKICAgIGdndGl0bGUoIk9QTFMtREEiKSArCiAgICBsYWJzKGNhcHRpb24gPSBUZVgoCiAgICAgIHBhc3RlMCgiJFJeezJ9X3tZfT0iLCBwbG90ZGF0YSRtb2RlbF9zdGF0cyRgUjJZKGN1bSlgLCAiJDsgIiwKICAgICAgICAgICAgICIkUV57Mn09IiwgcGxvdGRhdGEkbW9kZWxfc3RhdHMkYFEyKGN1bSlgLCAiJDsgIiwKICAgICAgICAgICAgICIkcF97UV57Mn19PSIsIHBsb3RkYXRhJG1vZGVsX3N0YXRzJHBRMiwgIiQiKSkpCn0KYGBgCgojIyBDcmVhdGUgQ3VzdG9tIFN1bW1hcnkgVGFibGUgRnVuY3Rpb24KYGBge3J9Cm15X3RhYmxlIDwtIGZ1bmN0aW9uKGRhdGEsIHBjYSwgcGxzZGEpewogIFZJUHMgPC0gZ2V0X1ZJUChwbHNkYSkKICAKICBsb2FkaW5nc19wbHMgPC0gZ2V0TG9hZGluZ01OKHBsc2RhKSAlPiUKICAgIGFzLmRhdGEuZnJhbWUoKSAlPiUKICAgIHJvd25hbWVzX3RvX2NvbHVtbih2YXIgPSAiVmFyaWFibGUiKSAlPiUgCiAgICByZW5hbWUocDFfbG9hZGluZyA9IHAxLCBwMl9sb2FkaW5nID0gcDIpCiAgCiAgbG9hZGluZ3NfcGNhIDwtIGdldExvYWRpbmdNTihwY2EpICU+JQogICAgYXMuZGF0YS5mcmFtZSgpICU+JSAKICAgIHJvd25hbWVzX3RvX2NvbHVtbih2YXIgPSAiVmFyaWFibGUiKSAlPiUgCiAgICBzZWxlY3QoVmFyaWFibGUsIFBDMV9sb2FkaW5nID0gcDEsIFBDMl9sb2FkaW5nID0gcDIpCiAgCiAgdF90ZXN0cyA8LSBkYXRhICU+JQogICAgc2VsZWN0KC1ncm91cCkgJT4lCiAgICBtYXBfZGYofnQudGVzdCgufmRhdGEkZ3JvdXApJHAudmFsdWUpICU+JQogICAgZ2F0aGVyKGtleSA9IFZhcmlhYmxlLCB2YWx1ZSA9IHRfdGVzdF9wLnZhbHVlKQogIAogIGpvaW4xIDwtIGZ1bGxfam9pbihWSVBzLCBsb2FkaW5nc19wbHMpCiAgam9pbjIgPC0gZnVsbF9qb2luKGpvaW4xLCBsb2FkaW5nc19wY2EpCiAgam9pbjMgPC0gZnVsbF9qb2luKGpvaW4yLCB0X3Rlc3RzKQogIHJldHVybihqb2luMykKfQpgYGAKCiMgQ3JlYXRlIHNhZmUgdmVyc2lvbiBvZiBvcGxzKCkKT2NjYXNpb25hbGx5IEkgd2FzIGdldHRpbmcgJ21hdHJpeCBpcyBzaW5ndWxhcicgdHlwZSBlcnJvcnMgZnJvbSBgb3BscygpYCwgd2hpY2ggSSB0aGluayBpcyBqdXN0IGEgY2hhbmNlIG9jY3VycmFuY2Ugd2l0aCB0aGUgcmFuZG9tIGRhdGEuICBUaGlzICJzYWZlIiB2ZXJzaW9uIHdpbGwgYWx3YXlzIHJldHVybiBgTlVMTGAgd2hlbiB0aGVyZSBpcyBhbnkga2luZCBvZiBlcnJvci4KYGBge3J9CnNhZmUub3BscyA8LSBwb3NzaWJseShvcGxzLCBvdGhlcndpc2UgPSBOVUxMKQpgYGAKCgojIFNldCBnbG9iYWwgb3B0aW9ucyAobmVlZGxlIGluIGhheXN0YWNrIHNjZW5hcmlvKQpJJ2xsIHNldCBhIGZldyBnbG9iYWwgb3B0aW9ucyBoZXJlIHRvIG1ha2UgaXQgZWFzaWVyIHRvIHBsYXkgYXJvdW5kIHdpdGggdGhlIHNpbXVsYXRpb24uCgpUaGVzZSBvcHRpb25zIGJhc2ljYWxseSBjcmVhdGUgYSBzaXR1YXRpb24gd2hlcmUgd2hlbiB0aGVyZSBhcmUgcmVhbCBkaWZmZXJlbmNlcyBiZXR3ZWVuIGdyb3VwcywgaXQncyBvbmx5IGR1ZSB0byBhIHNtYWxsIHBlcmNlbnRhZ2Ugb2YgdmFyaWFibGVzLiBPdGhlciB2YXJpYWJsZXMgY2FuIGhhdmUgbWlsZCBjb3ZhcmlhdGlvbiBvciBub25lIGF0IGFsbC4KYGBge3IgZ2xvYmFsIG9wdGlvbnN9Ck4gPSAzMCAjdG90YWwgbnVtYmVyIG9mIG9ic2VydmF0aW9ucwpQID0gNDAgI3RvdGFsIG51bWJlciBvZiB2YXJpYWJsZXMKc2lnbmFsLnByb3AgPSAwLjYyNSAjd2hlbiB0aGVyZSBhcmUgY29ycmVsYXRlZCB2YXJpYWJsZXMsIHdoYXQgcHJvcG9ydGlvbj8KY292X2NvcnZhciA9IDAuOCAjY292YXJpYW5jZSBmb3Igc2lnbmFsIHZhcmlhYmxlcwpkaXNjLnByb3AgPSAwLjEyNSAjd2hlbiB0aGVyZSBhcmUgZGlzY3JpbWluYXRpbmcgdmFyaWFibGVzLCB3aGF0IHByb3BvcnRpb24/CmRpZmZfZGlzY3IgPSAxLjIgI21lYW4gZGlmZmVyZW5jZSBiZXR3ZWVuIGdyb3VwcyBmb3IgZGlzY3JpbWluYXRpbmcgdmFyaWFibGVzCm5wZXJtID0gNTAgI2hvdyBtYW55IHBlcnVtdXRhdGlvbnMKc2VlZCA9IDEwMCAjc2VlZAoKI2NhbGN1bGF0ZWQgdmFsdWVzCihwX2NvcnZhciA9IHJvdW5kKFAqc2lnbmFsLnByb3ApKQoocF9kaXNjciA9IHJvdW5kKFAqZGlzYy5wcm9wKSkKYGBgCgoKIyAxLiBObyBDb3ZhcmlhbmNlLCBObyBEaXNjcmltaW5hdGlvbgojIyBHZW5lcmF0ZSBkYXRhCmBgYHtyfQpzaW0uZGF0YTEgPC0gc2ltX211bHR2YXIocF91bmNvcnZhciA9IFAsCiAgICAgICAgICAgICAgICAgICAgICBwX2NvcnZhciA9IDAsCiAgICAgICAgICAgICAgICAgICAgICBwX2Rpc2NyID0gMCwKICAgICAgICAgICAgICAgICAgICAgIGNvdl9jb3J2YXIgPSAwLAogICAgICAgICAgICAgICAgICAgICAgZGlmZl9kaXNjciA9IDAsCiAgICAgICAgICAgICAgICAgICAgICBOID0gTiwKICAgICAgICAgICAgICAgICAgICAgIHNlZWQgPSBzZWVkKQpzaW0uZGF0YTEgJT4lIHNlbGVjdCgtZ3JvdXApICU+JSBhcy5tYXRyaXgoKSAlPiUgY29yKCkgJT4lIGloZWF0bWFwKHJvd19sYWJlbHMgPSBUUlVFLCBjb2xfbGFiZWxzID0gVFJVRSkKYGBgCgojIyBQQ0EgJiBQTFMtREEKIyMjIFJ1biBQQ0EKYGBge3J9CnNpbS5wY2ExIDwtIG9wbHMoc2VsZWN0KHNpbS5kYXRhMSwgLWdyb3VwKSwgcGxvdEwgPSBGQUxTRSkKYGBgCiMjIyBSdW4gUExTLURBCmBgYHtyfQpzaW0ucGxzZGExIDwtIHRyeShvcGxzKHNlbGVjdChzaW0uZGF0YTEsIC1ncm91cCksIHNpbS5kYXRhMSRncm91cCwKICAgICAgICAgICAgICAgICAgcGxvdEwgPSBGQUxTRSkpCmBgYApUaGlzIGZhaWxzLCBidXQgSSdsbCBmb3JjZSB0d28gYXhlcyBmb3IgdGhlIHNha2Ugb2YgcGxvdHRpbmcuCmBgYHtyfQojIyBNdXN0IGZvcmNlIGF4ZXMKc2ltLnBsc2RhMSA8LSBvcGxzKHNlbGVjdChzaW0uZGF0YTEsIC1ncm91cCksIHNpbS5kYXRhMSRncm91cCwKICAgICAgICAgICAgICAgICAgIHByZWRJID0gMiwKICAgICAgICAgICAgICAgICAgIHBlcm1JID0gMjAwLAogICAgICAgICAgICAgICAgICAgcGxvdEwgPSBGQUxTRSkKYGBgCiMjIFBsb3RzOgpgYGB7ciBtZXNzYWdlPUZBTFNFLCB3YXJuaW5nPUZBTFNFfQpwMSA8LSBwbG90X2dyaWQocGNhX3Bsb3Qoc2ltLnBjYTEsIHNpbS5kYXRhMSRncm91cCkgKwogICAgICAgICAgICAgICAgIHRoZW1lKGxlZ2VuZC5wb3NpdGlvbiA9ICJub25lIikgKwogICAgICAgICAgICAgICAgIGdndGl0bGUoIlBDQSIsIHN1YnRpdGxlID0gIk5vIGNvdmFyaWFuY2UsIG5vIGRpc2NyaW1pbmF0aW5nIHZhcmlhYmxlcyIpLAogICAgICAgICAgICAgICBwbHNkYV9wbG90KHNpbS5wbHNkYTEpICsKICAgICAgICAgICAgICAgICB0aGVtZShsZWdlbmQucG9zaXRpb24gPSAibm9uZSIpICsKICAgICAgICAgICAgICAgICBnZ3RpdGxlKCJQTFMtREEiLCBzdWJ0aXRsZSA9ICJObyBjb3ZhcmlhbmNlLCBubyBkaXNjcmltaW5hdGluZyB2YXJpYWJsZXMiKSkKcDEKYGBgCgojIyBHZXQgUEMgYXhpcyBsb2FkaW5ncyBhbmQgVklQIHNjb3Jlcy4KTm90ZTogSXQncyBwcm9iYWJseSBub3QgdmVyeSByZXNwb25zaWJsZSB0byBsb29rIGF0IFZJUCBzY29yZXMgZnJvbSBhIFBMUy1EQSBtb2RlbCB0aGF0IGlzIG5vdCBzaWduaWZpY2FudApgYGB7cn0KbXlfdGFibGUoc2ltLmRhdGExLCBzaW0ucGNhMSwgc2ltLnBsc2RhMSkgJT4lIGFycmFuZ2UoZGVzYyhWSVApKQpgYGAKCiMjIFBlcm11dGF0aW9uIHRlc3RpbmcKRmlyc3QsIG1ha2UgYSBidW5jaCBvZiBkYXRhc2V0cyB3aXRoIHRoZSBzYW1lIHBhcmFtZXRlcnMKYGBge3J9CnNpbS5kYXRhMS5saXN0IDwtIG1hcCgxOm5wZXJtLAogICAgfnNpbV9tdWx0dmFyKHBfdW5jb3J2YXIgPSBQLAogICAgICAgICAgICAgIHBfY29ydmFyID0gMCwKICAgICAgICAgICAgICBwX2Rpc2NyID0gMCwKICAgICAgICAgICAgICBjb3ZfY29ydmFyID0gMCwKICAgICAgICAgICAgICBkaWZmX2Rpc2NyID0gMCwKICAgICAgICAgICAgICBOID0gTiwKICAgICAgICAgICAgICBzZWVkID0gTkEpKQpuYW1lcyhzaW0uZGF0YTEubGlzdCkgPC0gMTpucGVybQpgYGAKCk5vdywgZG8gUENBIG9uIGFsbCBvZiB0aGVtIGFuZCBnZXQgc2NvcmVzCmBgYHtyIG1lc3NhZ2U9RkFMU0UsIHdhcm5pbmc9RkFMU0UsIGluY2x1ZGU9RkFMU0V9CnBjYS5kYXRhMS5saXN0IDwtIG1hcChzaW0uZGF0YTEubGlzdCwKICAgICAgICAgICAgICAgICAgICAgIH5zYWZlLm9wbHMoc2VsZWN0KC4sIC1ncm91cCksIHBsb3RMID0gRkFMU0UpKSAlPiUgY29tcGFjdCgpCgpzY29yZXMuZGF0YTEubGlzdCA8LSBtYXAocGNhLmRhdGExLmxpc3QsIH5nZXRfcGxvdGRhdGEoLikgJT4lIC4kcGxvdF9kYXRhKQpgYGAKCk5vdyBkbyB0LXRlc3RzIG9uIGFsbCBvZiB0aGVtIGFsb25nIFBDcyAxIGFuZCAyIGFuZCBnZXQgcC12YWx1ZXMKYGBge3J9CiNUaGUgJ2dyb3VwJyB2YXJpYWJsZSBpcyB0aGUgc2FtZSBpbiBhbGwgZGF0YXNldHMuCmdyb3VwaW5nIDwtIHNpbS5kYXRhMS5saXN0W1sxXV0kZ3JvdXAKCnAxLnRlc3RyZXN1bHRzIDwtIHNjb3Jlcy5kYXRhMS5saXN0ICU+JQogICNtYXBzIHQudGVzdCgpIGZ1bmN0aW9uIHRvIGFsbCBkYXRhZnJhbWVzIGFuZCBjb252ZXJ0cyBvdXRwdXQgaW50byBkYXRhZnJhbWUgd2l0aCB0aWR5KCkKICBtYXBfZGZyKH50LnRlc3QoLiRwMSB+IGdyb3VwaW5nKSAlPiUgdGlkeSgpLCAuaWQgPSAiZGF0YXNldCIpICU+JSAKICAjc2VsZWN0cyBqdXN0IGNvbHVtbnMgb2YgaW50ZXJlc3QKICBzZWxlY3QoZGF0YXNldCwKICAgICAgICAgUEMxLmVmZmVjdC5zaXplID0gImVzdGltYXRlIiwKICAgICAgICAgUEMxLnQgPSAic3RhdGlzdGljIiwKICAgICAgICAgUEMxLnAudmFsdWUgPSAicC52YWx1ZSIpCgpwMi50ZXN0cmVzdWx0cyA8LSBzY29yZXMuZGF0YTEubGlzdCAlPiUKICBtYXBfZGZyKH50LnRlc3QoLiRwMiB+IGdyb3VwaW5nKSAlPiUgdGlkeSgpLCAuaWQgPSAiZGF0YXNldCIpICU+JQogIHNlbGVjdChkYXRhc2V0LAogICAgICAgICBQQzIuZWZmZWN0LnNpemUgPSAiZXN0aW1hdGUiLAogICAgICAgICBQQzIudCA9ICJzdGF0aXN0aWMiLAogICAgICAgICBQQzIucC52YWx1ZSA9ICJwLnZhbHVlIikKCmRhdGExLlBDQXJlc3VsdHMgPC0gYmluZF9jb2xzKHAxLnRlc3RyZXN1bHRzLCBwMi50ZXN0cmVzdWx0cykKYGBgCgpBbmQgZG8gUEVSTUFOT1ZBIG9uIGFsbCBvZiB0aGVtCmBgYHtyfQpkYXRhMS5wZXJtYW5vdmEgPC0gbWFwX2RibChzaW0uZGF0YTEubGlzdCwKICAgIH4gYWRvbmlzKHNlbGVjdCguLCAtZ3JvdXApIH4gZ3JvdXBpbmcsIG1ldGhvZCA9ICJldSIpJGFvdi50YWIkYFByKD5GKWBbMV0pCmBgYAoKCk5vdyBkbyBQTFMtREEgb24gYWxsIG9mIHRoZW0gYW5kIGdldCBwLXZhbHVlcyAodGhpcyB3aWxsIHRha2UgYSBsb25nIHRpbWUpCmBgYHtyIGluY2x1ZGU9RkFMU0V9CnBscy5kYXRhMS5saXN0IDwtIG1hcChzaW0uZGF0YTEubGlzdCwKICAgICAgICAgICAgICAgICAgICAgIH5zYWZlLm9wbHMoc2VsZWN0KC4sIC1ncm91cCksIGdyb3VwaW5nLCBwcmVkSSA9IDIsIHBlcm1JID0gMjAwLCBwbG90TCA9IEZBTFNFKSkKYGBgCmBgYHtyIG1lc3NhZ2U9RkFMU0UsIHdhcm5pbmc9RkFMU0V9CmRhdGExLlBMU3Jlc3VsdHMgPC0gcGxzLmRhdGExLmxpc3QgJT4lCiAgbWFwX2Rmcih+Z2V0X3Bsb3RkYXRhKC4pJG1vZGVsX3N0YXRzLCAuaWQgPSAiZGF0YXNldCIpCgoKZGF0YTEuY29tcGFyaXNvbiA8LSBmdWxsX2pvaW4oZGF0YTEuUENBcmVzdWx0cywgZGF0YTEuUExTcmVzdWx0cykgJT4lIGFkZF9jb2x1bW4oInBlcm1hbm92YSIgPSBkYXRhMS5wZXJtYW5vdmEpCgpwMS5wZXJtIDwtIGdncGxvdChkYXRhMS5jb21wYXJpc29uKSArCiAgZ2VvbV9kZW5zaXR5KGFlcyhQQzEucC52YWx1ZSksIGZpbGwgPSAiYmx1ZSIsIGFscGhhID0gMC4zMykgKwogIGdlb21fZGVuc2l0eShhZXMocFEyKSwgZmlsbCA9ICJyZWQiLCBhbHBoYSA9IDAuMzMpICsKICBnZW9tX2RlbnNpdHkoYWVzKHBlcm1hbm92YSksIGZpbGwgPSAiZ3JlZW4iLCBhbHBoYSA9IDAuMzMpICsKICBsYWJzKHggPSAicCB2YWx1ZSIpICsKICBnZW9tX3ZsaW5lKHhpbnRlcmNlcHQgPSAwLjA1LCBsaW5ldHlwZSA9IDUpICsKICBnZ3RpdGxlKCIxLiAtQ292YXJpYW5jZSwgLURpc2NyaW1pbmF0aW5nIHZhcmlhYmxlcyIpCnAxLnBlcm0KYGBgCgoKIyAyLiBZZXMgQ292YXJpYW5jZSwgTm8gRGlzY3JpbWluYXRpb24KIyMgR2VuZXJhdGUgRGF0YToKYGBge3J9CnNpbS5kYXRhMiA8LSBzaW1fbXVsdHZhcihwX3VuY29ydmFyID0gUCAtIHBfY29ydmFyLAogICAgICAgICAgICAgICAgICAgICAgcF9jb3J2YXIgPSBwX2NvcnZhciwKICAgICAgICAgICAgICAgICAgICAgIHBfZGlzY3IgPSAwLAogICAgICAgICAgICAgICAgICAgICAgY292X2NvcnZhciA9IGNvdl9jb3J2YXIsCiAgICAgICAgICAgICAgICAgICAgICBkaWZmX2Rpc2NyID0gMCwKICAgICAgICAgICAgICAgICAgICAgIE4gPSBOLAogICAgICAgICAgICAgICAgICAgICAgc2VlZCA9IHNlZWQpCgpzaW0uZGF0YTIgJT4lIHNlbGVjdCgtZ3JvdXApICU+JSBhcy5tYXRyaXgoKSAlPiUgY29yKCkgJT4lIGloZWF0bWFwKHJvd19sYWJlbHMgPSBUUlVFLCBjb2xfbGFiZWxzID0gVFJVRSkKYGBgCgojIyBQQ0EgJiBQTFMtREEKIyMjIFJ1biBQQ0EKYGBge3J9CnNpbS5wY2EyIDwtIG9wbHMoc2VsZWN0KHNpbS5kYXRhMiwgLWdyb3VwKSwgcGxvdEwgPSBGQUxTRSkKYGBgCiMjIyBSdW4gUExTLURBCmBgYHtyfQpzaW0ucGxzZGEyIDwtIHRyeShvcGxzKHNlbGVjdChzaW0uZGF0YTIsIC1ncm91cCksIHNpbS5kYXRhMiRncm91cCwKICAgICAgICAgICAgICAgICAgcGxvdEwgPSBGQUxTRSkpCmBgYApUaGlzIGZhaWxzLCBidXQgSSdsbCBmb3JjZSB0d28gYXhlcyBmb3IgdGhlIHNha2Ugb2YgcGxvdHRpbmcuCmBgYHtyfQojIyBNdXN0IGZvcmNlIGF4ZXMKc2ltLnBsc2RhMiA8LSBvcGxzKHNlbGVjdChzaW0uZGF0YTIsIC1ncm91cCksIHNpbS5kYXRhMiRncm91cCwKICAgICAgICAgICAgICAgICAgIHByZWRJID0gMiwKICAgICAgICAgICAgICAgICAgIHBlcm1JID0gMjAwLCBwbG90TCA9IEZBTFNFKQpgYGAKIyMjIFBsb3RzOgpgYGB7ciBtZXNzYWdlPUZBTFNFLCB3YXJuaW5nPUZBTFNFfQpwMiA8LSBwbG90X2dyaWQocGNhX3Bsb3Qoc2ltLnBjYTIsIHNpbS5kYXRhMiRncm91cCkgKwogICAgICAgICAgICAgICAgIHRoZW1lKGxlZ2VuZC5wb3NpdGlvbiA9ICJub25lIikgKwogICAgICAgICAgICAgICAgIGdndGl0bGUoIlBDQSIsIHN1YnRpdGxlID0gIlllcyBjb3ZhcmlhbmNlLCBubyBkaXNjcmltaW5hdGluZyB2YXJpYWJsZXMiKSwKICAgICAgICAgICAgICAgcGxzZGFfcGxvdChzaW0ucGxzZGEyKSArCiAgICAgICAgICAgICAgICAgdGhlbWUobGVnZW5kLnBvc2l0aW9uID0gIm5vbmUiKSArCiAgICAgICAgICAgICAgICAgZ2d0aXRsZSgiUExTLURBIiwgc3VidGl0bGUgPSAiWWVzIGNvdmFyaWFuY2UsIG5vIGRpc2NyaW1pbmF0aW5nIHZhcmlhYmxlcyIpKQpwMgpgYGAKUEMxIHNsaWdodGx5IGJldHRlciwgYnV0IG92ZXJhbGwgUl4yIHRoZSBzYW1lLiBQTFMtREEgc3RpbGwgbm90IHNpZ25pZmljYW50CgojIyBHZXQgUEMgYXhpcyBsb2FkaW5ncyBhbmQgVklQIHNjb3Jlcy4KTm90ZTogSXQncyBwcm9iYWJseSBub3QgcmVzcG9uc2libGUgdG8gbG9vayBhdCBWSVAgc2NvcmVzIGZyb20gYSBQTFMtREEgbW9kZWwgdGhhdCBpcyBub3Qgc2lnbmlmaWNhbnQKYGBge3J9Cm15X3RhYmxlKHNpbS5kYXRhMiwgc2ltLnBjYTIsIHNpbS5wbHNkYTIpICU+JSBhcnJhbmdlKGRlc2MoVklQKSkKYGBgCgojIyBQZXJtdXRhdGlvbiB0ZXN0aW5nCkZpcnN0LCBtYWtlIGEgYnVuY2ggb2YgZGF0YXNldHMgd2l0aCB0aGUgc2FtZSBwYXJhbWV0ZXJzCmBgYHtyfQpzaW0uZGF0YTIubGlzdCA8LSBtYXAoMTpucGVybSwKICAgICAgICAgICAgICAgICAgICAgIH5zaW1fbXVsdHZhcihwX3VuY29ydmFyID0gUCAtIHBfY29ydmFyLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHBfY29ydmFyID0gcF9jb3J2YXIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgcF9kaXNjciA9IDAsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgY292X2NvcnZhciA9IGNvdl9jb3J2YXIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgZGlmZl9kaXNjciA9IDAsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgTiA9IE4sCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgc2VlZCA9IE5BKSkKbmFtZXMoc2ltLmRhdGEyLmxpc3QpIDwtIDE6bnBlcm0KYGBgCgpOb3csIGRvIFBDQSBvbiBhbGwgb2YgdGhlbSBhbmQgZ2V0IGxvYWRpbmdzLgpgYGB7ciBtZXNzYWdlPUZBTFNFLCB3YXJuaW5nPUZBTFNFLCBpbmNsdWRlPUZBTFNFfQpwY2EuZGF0YTIubGlzdCA8LSBtYXAoc2ltLmRhdGEyLmxpc3QsCiAgICAgICAgICAgICAgICAgICAgICB+c2FmZS5vcGxzKHNlbGVjdCguLCAtZ3JvdXApLCBwbG90TCA9IEZBTFNFKSkgJT4lIGNvbXBhY3QoKQoKc2NvcmVzLmRhdGEyLmxpc3QgPC0gbWFwKHBjYS5kYXRhMi5saXN0LCB+Z2V0X3Bsb3RkYXRhKC4pICU+JSAuJHBsb3RfZGF0YSkKYGBgCgpOb3cgZG8gdC10ZXN0cyBvbiBhbGwgb2YgdGhlbSBhbG9uZyBQQ3MgMSBhbmQgMiBhbmQgZ2V0IHAtdmFsdWVzCmBgYHtyfQojVGhlICdncm91cCcgdmFyaWFibGUgaXMgdGhlIHNhbWUgaW4gYWxsIGRhdGFzZXRzLgpncm91cGluZyA8LSBzaW0uZGF0YTIubGlzdFtbMV1dJGdyb3VwCgpwMS50ZXN0cmVzdWx0cyA8LSBzY29yZXMuZGF0YTIubGlzdCAlPiUKICAjbWFwcyB0LnRlc3QoKSBmdW5jdGlvbiB0byBhbGwgZGF0YWZyYW1lcyBhbmQgY29udmVydHMgb3V0cHV0IGludG8gZGF0YWZyYW1lIHdpdGggdGlkeSgpCiAgbWFwX2Rmcih+dC50ZXN0KC4kcDF+Z3JvdXBpbmcpICU+JSB0aWR5KCksIC5pZCA9ICJkYXRhc2V0IikgJT4lIAogICNzZWxlY3RzIGp1c3QgY29sdW1ucyBvZiBpbnRlcmVzdAogIHNlbGVjdChkYXRhc2V0LAogICAgICAgICBQQzEuZWZmZWN0LnNpemUgPSAiZXN0aW1hdGUiLAogICAgICAgICBQQzEudCA9ICJzdGF0aXN0aWMiLAogICAgICAgICBQQzEucC52YWx1ZSA9ICJwLnZhbHVlIikKCiMgcDIudGVzdHJlc3VsdHMgPC0gc2NvcmVzLmRhdGEyLmxpc3QgJT4lCiMgICBtYXBfZGZyKH50LnRlc3QoLiRwMn5ncm91cGluZykgJT4lIHRpZHkoKSwgLmlkID0gImRhdGFzZXQiKSAlPiUKIyAgIHNlbGVjdChkYXRhc2V0LAojICAgICAgICAgIFBDMi5lZmZlY3Quc2l6ZSA9ICJlc3RpbWF0ZSIsCiMgICAgICAgICAgUEMyLnQgPSAic3RhdGlzdGljIiwKIyAgICAgICAgICBQQzIucC52YWx1ZSA9ICJwLnZhbHVlIikKCiMgZGF0YTIuUENBcmVzdWx0cyA8LSBiaW5kX2NvbHMocDEudGVzdHJlc3VsdHMsIHAyLnRlc3RyZXN1bHRzKQpkYXRhMi5QQ0FyZXN1bHRzIDwtIHAxLnRlc3RyZXN1bHRzCmBgYAoKQW5kIGRvIFBFUk1BTk9WQSBvbiBhbGwgb2YgdGhlbQpgYGB7cn0KZGF0YTIucGVybWFub3ZhIDwtIG1hcF9kYmwoc2ltLmRhdGEyLmxpc3QsCiAgICB+IGFkb25pcyhzZWxlY3QoLiwgLWdyb3VwKSB+IGdyb3VwaW5nLCBtZXRob2QgPSAiZXUiKSRhb3YudGFiJGBQcig+RilgWzFdKQpgYGAKCk5vdyBkbyBQTFMtREEgb24gYWxsIG9mIHRoZW0gYW5kIGdldCBwLXZhbHVlcyAodGhpcyB3aWxsIHRha2UgYSBsb25nIHRpbWUpCmBgYHtyIGluY2x1ZGU9RkFMU0V9CnBscy5kYXRhMi5saXN0IDwtIG1hcChzaW0uZGF0YTIubGlzdCwKICAgICAgICAgICAgICAgICAgICAgIH5zYWZlLm9wbHMoc2VsZWN0KC4sIC1ncm91cCksIGdyb3VwaW5nLCBwcmVkSSA9IDIsIHBlcm1JID0gMjAwLCBwbG90TCA9IEZBTFNFKSkgJT4lIGNvbXBhY3QoKQpgYGAKYGBge3IgbWVzc2FnZT1GQUxTRSwgd2FybmluZz1GQUxTRX0KZGF0YTIuUExTcmVzdWx0cyA8LSBwbHMuZGF0YTIubGlzdCAlPiUgbWFwX2Rmcih+Z2V0X3Bsb3RkYXRhKC4pJG1vZGVsX3N0YXRzLCAuaWQgPSAiZGF0YXNldCIpCmRhdGEyLlBMU3Jlc3VsdHMKCmRhdGEyLmNvbXBhcmlzb24gPC0gZnVsbF9qb2luKGRhdGEyLlBDQXJlc3VsdHMsIGRhdGEyLlBMU3Jlc3VsdHMpICU+JQogIGFkZF9jb2x1bW4oInBlcm1hbm92YSIgPSBkYXRhMi5wZXJtYW5vdmEpCgpwMi5wZXJtIDwtIGdncGxvdChkYXRhMi5jb21wYXJpc29uKSArCiAgZ2VvbV9kZW5zaXR5KGFlcyhQQzEucC52YWx1ZSksIGZpbGwgPSAiYmx1ZSIsIGFscGhhID0gMC4zMykgKwogIGdlb21fZGVuc2l0eShhZXMocFEyKSwgZmlsbCA9ICJyZWQiLCBhbHBoYSA9IDAuMzMpICsKICBnZW9tX2RlbnNpdHkoYWVzKHBlcm1hbm92YSksIGZpbGwgPSAiZ3JlZW4iLCBhbHBoYSA9IDAuMzMpICsKICBsYWJzKHggPSAicCB2YWx1ZSIpICsKICBnZW9tX3ZsaW5lKHhpbnRlcmNlcHQgPSAwLjA1LCBsaW5ldHlwZSA9IDUpICsKICBnZ3RpdGxlKCIyLiArIENvdmFyaWFuY2UsIC0gRGlzY3JpbWluYXRpbmcgdmFyaWFibGVzIikKcDIucGVybQpgYGAKCiMgMy4gTm8gQ292YXJpYW5jZSwgWWVzIERpc2NyaW1pbmF0aW9uCiMjIEdlbmVyYXRlIERhdGE6CmBgYHtyfQpzaW0uZGF0YTMgPC0gc2ltX211bHR2YXIocF91bmNvcnZhciA9IFAgLSBwX2Rpc2NyLAogICAgICAgICAgICAgICAgICAgICAgICAgcF9jb3J2YXIgPSAwLAogICAgICAgICAgICAgICAgICAgICAgICAgcF9kaXNjciA9IHBfZGlzY3IsCiAgICAgICAgICAgICAgICAgICAgICAgICBjb3ZfY29ydmFyID0gY292X2NvcnZhciwKICAgICAgICAgICAgICAgICAgICAgICAgIGRpZmZfZGlzY3IgPSBkaWZmX2Rpc2NyLAogICAgICAgICAgICAgICAgICAgICAgICAgY292X2Rpc2NyID0gMC42LAogICAgICAgICAgICAgICAgICAgICAgICAgTiA9IE4sCiAgICAgICAgICAgICAgICAgICAgICAgICBzZWVkID0gc2VlZCkKc2ltLmRhdGEzICU+JSBzZWxlY3QoLWdyb3VwKSAlPiUgYXMubWF0cml4KCkgJT4lIGNvcigpICU+JSBpaGVhdG1hcChyb3dfbGFiZWxzID0gVFJVRSwgY29sX2xhYmVscyA9IFRSVUUpCmBgYAoKIyMgUENBICYgUExTLURBCiMjIyBSdW4gUENBCmBgYHtyfQpzaW0ucGNhMyA8LSBvcGxzKHNlbGVjdChzaW0uZGF0YTMsIC1ncm91cCksIHBsb3RMID0gRkFMU0UpCmBgYAojIyMgUnVuIFBMUy1EQQpgYGB7cn0Kc2ltLnBsc2RhMyA8LSBvcGxzKHNlbGVjdChzaW0uZGF0YTMsIC1ncm91cCksIHNpbS5kYXRhMyRncm91cCwKICAgICAgICAgICAgICAgICAgIHBlcm1JID0gMjAwLAogICAgICAgICAgICAgICAgICAgcGxvdEwgPSBGQUxTRSkKYGBgClNpbmdsZSBjb21wb25lbnQgbW9kZWwgb25seSwgc28gZm9yY2UgdHdvIGF4ZXMgZm9yIHRoZSBzYWtlIG9mIHBsb3R0aW5nOgpgYGB7cn0Kc2ltLnBsc2RhMyA8LSBvcGxzKHNlbGVjdChzaW0uZGF0YTMsIC1ncm91cCksIHNpbS5kYXRhMyRncm91cCwKICAgICAgICAgICAgICAgICAgIHBlcm1JID0gMjAwLAogICAgICAgICAgICAgICAgICAgcHJlZEkgPSAyLAogICAgICAgICAgICAgICAgICAgcGxvdEwgPSBGQUxTRSkKYGBgCgojIyMgUGxvdHM6CmBgYHtyIG1lc3NhZ2U9RkFMU0UsIHdhcm5pbmc9RkFMU0V9CnAzIDwtIHBsb3RfZ3JpZChwY2FfcGxvdChzaW0ucGNhMywgc2ltLmRhdGEzJGdyb3VwKSArCiAgICAgICAgICAgICAgICAgdGhlbWUobGVnZW5kLnBvc2l0aW9uID0gIm5vbmUiKSArCiAgICAgICAgICAgICAgICAgZ2d0aXRsZSgiUENBIiwgc3VidGl0bGUgPSAiTm8gY292YXJpYW5jZSwgeWVzIGRpc2NyaW1pbmF0aW5nIHZhcmlhYmxlcyIpLAogICAgICAgICAgICAgICBwbHNkYV9wbG90KHNpbS5wbHNkYTMpICsKICAgICAgICAgICAgICAgICB0aGVtZShsZWdlbmQucG9zaXRpb24gPSAibm9uZSIpICsKICAgICAgICAgICAgICAgICBnZ3RpdGxlKCJQTFMtREEiLCBzdWJ0aXRsZSA9ICJObyBjb3ZhcmlhbmNlLCB5ZXMgZGlzY3JpbWluYXRpbmcgdmFyaWFibGVzIikpCnAzCmBgYApQTFMtREEgaXMgc3RpbGwgbm90IGdyZWF0LiAgU2luZ2xlIGNvbXBvbmVudCBtb2RlbCBpcyBzaWduaWZjYW50LiAgRm9yY2VkIDIgYXhlcyBmb3IgUExTLURBIHBsb3QuICBJdCBtaWdodCBiZSB0aGUgY2FzZSB0aGF0IHNvbWUgY28tdmFyaWFuY2UgYmV0d2VlbiBkaXNjcmltaW5hdGluZyB2YXJpYWJsZXMgaXMgcmVxdWlyZWQgZm9yIFBMUy1EQSB0byBwdWxsIHRoZW0gb3V0LiAgSWYgdGhleSBhcmUgY29tcGxldGVseSBvcnRob2dvbmFsLCBob3cgY2FuIGl0IGRyYXcgdGhlICJyZWdyZXNzaW9uIGxpbmUiIHRocm91Z2ggNS1kaW1lbnNpb25hbCBzcGFjZT8KCiMjIEdldCBQQyBheGlzIGxvYWRpbmdzIGFuZCBWSVAgc2NvcmVzLgpgYGB7cn0KbXlfdGFibGUoc2ltLmRhdGEzLCBzaW0ucGNhMywgc2ltLnBsc2RhMykgJT4lIGFycmFuZ2UoZGVzYyhWSVApKQpgYGAKCiMjIFBlcm11dGF0aW9uIHRlc3RpbmcKRmlyc3QsIG1ha2UgYSBidW5jaCBvZiBkYXRhc2V0cyB3aXRoIHRoZSBzYW1lIHBhcmFtZXRlcnMKYGBge3J9CnNpbS5kYXRhMy5saXN0IDwtIG1hcCgxOm5wZXJtLAogICAgICAgICAgICAgICAgICAgICAgfnNpbV9tdWx0dmFyKHBfdW5jb3J2YXIgPSBQIC0gcF9kaXNjciwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBwX2NvcnZhciA9IDAsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgcF9kaXNjciA9IHBfZGlzY3IsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgY292X2NvcnZhciA9IDAsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgZGlmZl9kaXNjciA9IGRpZmZfZGlzY3IsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgY292X2Rpc2NyID0gMC42LAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIE4gPSBOLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHNlZWQgPSBOQSkpCmBgYAoKTm93LCBkbyBQQ0Egb24gYWxsIG9mIHRoZW0gYW5kIGdldCBsb2FkaW5ncy4KYGBge3IgbWVzc2FnZT1GQUxTRSwgd2FybmluZz1GQUxTRSwgaW5jbHVkZT1GQUxTRX0KcGNhLmRhdGEzLmxpc3QgPC0gbWFwKHNpbS5kYXRhMy5saXN0LAogICAgICAgICAgICAgICAgICAgICAgfnNhZmUub3BscyhzZWxlY3QoLiwgLWdyb3VwKSwgcGxvdEwgPSBGQUxTRSkpICU+JQogIGNvbXBhY3QoKSAlPiUKICBzZXRfbmFtZXMoMTpucGVybSkKCnNjb3Jlcy5kYXRhMy5saXN0IDwtIG1hcChwY2EuZGF0YTMubGlzdCwgfmdldF9wbG90ZGF0YSguKSAlPiUgLiRwbG90X2RhdGEpCmBgYAoKTm93IGRvIHQtdGVzdHMgb24gYWxsIG9mIHRoZW0gYWxvbmcgUENzIDEgYW5kIDIgYW5kIGdldCBwLXZhbHVlcwpgYGB7cn0KI1RoZSAnZ3JvdXAnIHZhcmlhYmxlIGlzIHRoZSBzYW1lIGluIGFsbCBkYXRhc2V0cy4KZ3JvdXBpbmcgPC0gc2ltLmRhdGEzLmxpc3RbWzFdXSRncm91cAoKcDEudGVzdHJlc3VsdHMgPC0gc2NvcmVzLmRhdGEzLmxpc3QgJT4lCiAgI21hcHMgdC50ZXN0KCkgZnVuY3Rpb24gdG8gYWxsIGRhdGFmcmFtZXMgYW5kIGNvbnZlcnRzIG91dHB1dCBpbnRvIGRhdGFmcmFtZSB3aXRoIHRpZHkoKQogIG1hcF9kZnIofnQudGVzdCguJHAxfmdyb3VwaW5nKSAlPiUgdGlkeSgpLCAuaWQgPSAiZGF0YXNldCIpICU+JSAKICAjc2VsZWN0cyBqdXN0IGNvbHVtbnMgb2YgaW50ZXJlc3QKICBzZWxlY3QoZGF0YXNldCwKICAgICAgICAgUEMxLmVmZmVjdC5zaXplID0gImVzdGltYXRlIiwKICAgICAgICAgUEMxLnQgPSAic3RhdGlzdGljIiwKICAgICAgICAgUEMxLnAudmFsdWUgPSAicC52YWx1ZSIpCgpwMi50ZXN0cmVzdWx0cyA8LSBzY29yZXMuZGF0YTMubGlzdCAlPiUKICBtYXBfZGZyKH50LnRlc3QoLiRwMn5ncm91cGluZykgJT4lIHRpZHkoKSwgLmlkID0gImRhdGFzZXQiKSAlPiUKICBzZWxlY3QoZGF0YXNldCwKICAgICAgICAgUEMyLmVmZmVjdC5zaXplID0gImVzdGltYXRlIiwKICAgICAgICAgUEMyLnQgPSAic3RhdGlzdGljIiwKICAgICAgICAgUEMyLnAudmFsdWUgPSAicC52YWx1ZSIpCgpkYXRhMy5QQ0FyZXN1bHRzIDwtIGJpbmRfY29scyhwMS50ZXN0cmVzdWx0cywgcDIudGVzdHJlc3VsdHMpCmBgYAoKQW5kIGRvIFBFUk1BTk9WQSBvbiBhbGwgb2YgdGhlbQpgYGB7cn0KZGF0YTMucGVybWFub3ZhIDwtIG1hcF9kYmwoc2ltLmRhdGEzLmxpc3QsCiAgICB+IGFkb25pcyhzZWxlY3QoLiwgLWdyb3VwKSB+IGdyb3VwaW5nLCBtZXRob2QgPSAiZXUiKSRhb3YudGFiJGBQcig+RilgWzFdKQpgYGAKCk5vdyBkbyBQTFMtREEgb24gYWxsIG9mIHRoZW0gYW5kIGdldCBwLXZhbHVlcyAodGhpcyB3aWxsIHRha2UgYSBsb25nIHRpbWUpCmBgYHtyIGluY2x1ZGU9RkFMU0V9CnBscy5kYXRhMy5saXN0IDwtIG1hcChzaW0uZGF0YTMubGlzdCwKICAgICAgICAgICAgICAgICAgICAgIH5zYWZlLm9wbHMoc2VsZWN0KC4sIC1ncm91cCksIGdyb3VwaW5nLCBwcmVkSSA9IDIsIHBlcm1JID0gMjAwLCBwbG90TCA9IEZBTFNFKSkgJT4lIGNvbXBhY3QoKQpgYGAKYGBge3IgbWVzc2FnZT1GQUxTRSwgd2FybmluZz1GQUxTRX0KZGF0YTMuUExTcmVzdWx0cyA8LSBwbHMuZGF0YTMubGlzdCAlPiUgbWFwX2Rmcih+Z2V0X3Bsb3RkYXRhKC4pJG1vZGVsX3N0YXRzLCAuaWQgPSAiZGF0YXNldCIpCmRhdGEzLlBMU3Jlc3VsdHMKCmRhdGEzLmNvbXBhcmlzb24gPC0gZnVsbF9qb2luKGRhdGEzLlBDQXJlc3VsdHMsIGRhdGEzLlBMU3Jlc3VsdHMpICU+JSBhZGRfY29sdW1uKCJwZXJtYW5vdmEiID0gZGF0YTMucGVybWFub3ZhKQoKcDMucGVybSA8LSBnZ3Bsb3QoZGF0YTMuY29tcGFyaXNvbikgKwogIGdlb21fZGVuc2l0eShhZXMoUEMxLnAudmFsdWUpLCBmaWxsID0gImJsdWUiLCBhbHBoYSA9IDAuMzMpICsKICBnZW9tX2RlbnNpdHkoYWVzKHBRMiksIGZpbGwgPSAicmVkIiwgYWxwaGEgPSAwLjMzKSArCiAgZ2VvbV9kZW5zaXR5KGFlcyhwZXJtYW5vdmEpLCBmaWxsID0gImdyZWVuIiwgYWxwaGEgPSAwLjMzKSArCiAgbGFicyh4ID0gInAgdmFsdWUiKSArCiAgZ2VvbV92bGluZSh4aW50ZXJjZXB0ID0gMC4wNSwgbGluZXR5cGUgPSA1KSArCiAgZ2d0aXRsZSgiMy4gLSBDb3ZhcmlhbmNlLCArIERpc2NyaW1pbmF0aW5nIHZhcmlhYmxlcyIpCnAzLnBlcm0KYGBgCgojIDQuIFllcyBDb3ZhcmlhbmNlLCBZZXMgRGlzY3JpbWluYXRpb24KIyMgR2VuZXJhdGUgRGF0YToKYGBge3J9CnNpbS5kYXRhNCA8LSBzaW1fbXVsdHZhcihwX3VuY29ydmFyID0gUCAtIHBfY29ydmFyIC0gcF9kaXNjciwKICAgICAgICAgICAgICAgICAgICAgICAgIHBfY29ydmFyID0gcF9jb3J2YXIsCiAgICAgICAgICAgICAgICAgICAgICAgICBwX2Rpc2NyID0gcF9kaXNjciwKICAgICAgICAgICAgICAgICAgICAgICAgIGNvdl9jb3J2YXIgPSBjb3ZfY29ydmFyLAogICAgICAgICAgICAgICAgICAgICAgICAgZGlmZl9kaXNjciA9IGRpZmZfZGlzY3IsCiAgICAgICAgICAgICAgICAgICAgICAgICBjb3ZfZGlzY3IgPSAwLjYsCiAgICAgICAgICAgICAgICAgICAgICAgICBOID0gTiwKICAgICAgICAgICAgICAgICAgICAgICAgIHNlZWQgPSBzZWVkKQpzaW0uZGF0YTQgJT4lIHNlbGVjdCgtZ3JvdXApICU+JSBhcy5tYXRyaXgoKSAlPiUgY29yKCkgJT4lIGloZWF0bWFwKHJvd19sYWJlbHMgPSBUUlVFLCBjb2xfbGFiZWxzID0gVFJVRSkKYGBgCgojIyBQQ0EgJiBQTFMtREEKIyMjIFJ1biBQQ0EKYGBge3J9CnNpbS5wY2E0IDwtIG9wbHMoc2VsZWN0KHNpbS5kYXRhNCwgLWdyb3VwKSwgcGxvdEwgPSBGQUxTRSkKYGBgCiMjIyBSdW4gUExTLURBCmBgYHtyfQpzaW0ucGxzZGE0IDwtIG9wbHMoc2VsZWN0KHNpbS5kYXRhNCwgLWdyb3VwKSwgc2ltLmRhdGE0JGdyb3VwLAogICAgICAgICAgICAgICAgICAgcGVybUkgPSAyMDAsCiAgICAgICAgICAgICAgICAgICBwbG90TCA9IEZBTFNFKQpgYGAKVGhpcyBwcm9kdWNlcyBhIDEtY29tcG9uZW50IG1vZGVsLiAgSSdsbCBmb3JjZSB0d28gYXhlcyBmb3IgdGhlIHNha2Ugb2YgcGxvdHRpbmc6CmBgYHtyfQpzaW0ucGxzZGE0IDwtIG9wbHMoc2VsZWN0KHNpbS5kYXRhNCwgLWdyb3VwKSwgc2ltLmRhdGE0JGdyb3VwLAogICAgICAgICAgICAgICAgICAgcGVybUkgPSAyMDAsCiAgICAgICAgICAgICAgICAgICBwcmVkSSA9IDIsCiAgICAgICAgICAgICAgICAgICBwbG90TCA9IEZBTFNFKQpgYGAKCiMjIyBQbG90czoKYGBge3IgbWVzc2FnZT1GQUxTRSwgd2FybmluZz1GQUxTRX0KcDQgPC0gcGxvdF9ncmlkKHBjYV9wbG90KHNpbS5wY2E0LCBzaW0uZGF0YTQkZ3JvdXApICsKICAgICAgICAgICAgICAgICB0aGVtZShsZWdlbmQucG9zaXRpb24gPSAibm9uZSIpICsKICAgICAgICAgICAgICAgICBnZ3RpdGxlKCJQQ0EiLCBzdWJ0aXRsZSA9ICJZZXMgY292YXJpYW5jZSwgWWVzIGRpc2NyaW1pbmF0aW5nIHZhcmlhYmxlcyIpLAogICAgICAgICAgICAgICBwbHNkYV9wbG90KHNpbS5wbHNkYTQpICsKICAgICAgICAgICAgICAgICB0aGVtZShsZWdlbmQucG9zaXRpb24gPSAibm9uZSIpICsKICAgICAgICAgICAgICAgICBnZ3RpdGxlKCJQTFMtREEiLCBzdWJ0aXRsZSA9ICJZZXMgY292YXJpYW5jZSwgWWVzIGRpc2NyaW1pbmF0aW5nIHZhcmlhYmxlcyIpKQpwNApgYGAKCgojIyBHZXQgUEMgYXhpcyBsb2FkaW5ncyBhbmQgVklQIHNjb3Jlcy4KYGBge3J9Cm15X3RhYmxlKHNpbS5kYXRhNCwgc2ltLnBjYTQsIHNpbS5wbHNkYTQpICU+JSBhcnJhbmdlKGRlc2MoVklQKSkKYGBgCgojIyBQZXJtdXRhdGlvbiB0ZXN0aW5nCkZpcnN0LCBtYWtlIGEgYnVuY2ggb2YgZGF0YXNldHMgd2l0aCB0aGUgc2FtZSBwYXJhbWV0ZXJzCmBgYHtyfQpzaW0uZGF0YTQubGlzdCA8LSBtYXAoMTpucGVybSwKICAgICAgICAgICAgICAgICAgICAgIH5zaW1fbXVsdHZhcihwX3VuY29ydmFyID0gUCAtIHBfY29ydmFyIC0gcF9kaXNjciwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBwX2NvcnZhciA9IHBfY29ydmFyLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHBfZGlzY3IgPSBwX2Rpc2NyLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGNvdl9jb3J2YXIgPSBjb3ZfY29ydmFyLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGRpZmZfZGlzY3IgPSBkaWZmX2Rpc2NyLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGNvdl9kaXNjciA9IDAuMiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBOID0gTiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBzZWVkID0gTkEpKSAlPiUgCiAgc2V0X25hbWVzKDE6bnBlcm0pCmBgYAoKTm93LCBkbyBQQ0Egb24gYWxsIG9mIHRoZW0gYW5kIGdldCBsb2FkaW5ncy4KYGBge3IgbWVzc2FnZT1GQUxTRSwgd2FybmluZz1GQUxTRSwgaW5jbHVkZT1GQUxTRX0KcGNhLmRhdGE0Lmxpc3QgPC0gbWFwKHNpbS5kYXRhNC5saXN0LAogICAgICAgICAgICAgICAgICAgICAgfnNhZmUub3BscyhzZWxlY3QoLiwgLWdyb3VwKSwgcGxvdEwgPSBGQUxTRSkpICU+JQogIGNvbXBhY3QoKQpzY29yZXMuZGF0YTQubGlzdCA8LSBtYXAocGNhLmRhdGE0Lmxpc3QsIH5nZXRfcGxvdGRhdGEoLikgJT4lIC4kcGxvdF9kYXRhKQpgYGAKCk5vdyBkbyB0LXRlc3RzIG9uIGFsbCBvZiB0aGVtIGFsb25nIFBDcyAxIGFuZCAyIGFuZCBnZXQgcC12YWx1ZXMKYGBge3J9CiNUaGUgJ2dyb3VwJyB2YXJpYWJsZSBpcyB0aGUgc2FtZSBpbiBhbGwgZGF0YXNldHMuCmdyb3VwaW5nIDwtIHNpbS5kYXRhNC5saXN0W1sxXV0kZ3JvdXAKCnAxLnRlc3RyZXN1bHRzIDwtIHNjb3Jlcy5kYXRhNC5saXN0ICU+JQogICNtYXBzIHQudGVzdCgpIGZ1bmN0aW9uIHRvIGFsbCBkYXRhZnJhbWVzIGFuZCBjb252ZXJ0cyBvdXRwdXQgaW50byBkYXRhZnJhbWUgd2l0aCB0aWR5KCkKICBtYXBfZGZyKH50LnRlc3QoLiRwMX5ncm91cGluZykgJT4lIHRpZHkoKSwgLmlkID0gImRhdGFzZXQiKSAlPiUgCiAgI3NlbGVjdHMganVzdCBjb2x1bW5zIG9mIGludGVyZXN0CiAgc2VsZWN0KGRhdGFzZXQsCiAgICAgICAgIFBDMS5lZmZlY3Quc2l6ZSA9ICJlc3RpbWF0ZSIsCiAgICAgICAgIFBDMS50ID0gInN0YXRpc3RpYyIsCiAgICAgICAgIFBDMS5wLnZhbHVlID0gInAudmFsdWUiKQoKIyBwMi50ZXN0cmVzdWx0cyA8LSBzY29yZXMuZGF0YTQubGlzdCAlPiUKIyAgIG1hcF9kZnIofnQudGVzdCguJHAyfmdyb3VwaW5nKSAlPiUgdGlkeSgpLCAuaWQgPSAiZGF0YXNldCIpICU+JQojICAgc2VsZWN0KGRhdGFzZXQsCiMgICAgICAgICAgUEMyLmVmZmVjdC5zaXplID0gImVzdGltYXRlIiwKIyAgICAgICAgICBQQzIudCA9ICJzdGF0aXN0aWMiLAojICAgICAgICAgIFBDMi5wLnZhbHVlID0gInAudmFsdWUiKQoKIyBkYXRhNC5QQ0FyZXN1bHRzIDwtIGJpbmRfY29scyhwMS50ZXN0cmVzdWx0cywgcDIudGVzdHJlc3VsdHMpCmRhdGE0LlBDQXJlc3VsdHMgPC0gcDEudGVzdHJlc3VsdHMKYGBgCgpBbmQgZG8gUEVSTUFOT1ZBIG9uIGFsbCBvZiB0aGVtCmBgYHtyfQpkYXRhNC5wZXJtYW5vdmEgPC0gbWFwX2RibChzaW0uZGF0YTQubGlzdCwKICAgIH4gYWRvbmlzKHNlbGVjdCguLCAtZ3JvdXApIH4gZ3JvdXBpbmcsIG1ldGhvZCA9ICJldSIpJGFvdi50YWIkYFByKD5GKWBbMV0pCmBgYAoKTm93IGRvIFBMUy1EQSBvbiBhbGwgb2YgdGhlbSBhbmQgZ2V0IHAtdmFsdWVzICh0aGlzIHdpbGwgdGFrZSBhIGxvbmcgdGltZSkKYGBge3IgaW5jbHVkZT1GQUxTRX0KcGxzLmRhdGE0Lmxpc3QgPC0gbWFwKHNpbS5kYXRhNC5saXN0LAogICAgICAgICAgICAgICAgICAgICAgfnNhZmUub3BscyhzZWxlY3QoLiwgLWdyb3VwKSwgZ3JvdXBpbmcsIHByZWRJID0gMiwgcGVybUkgPSAyMDAsIHBsb3RMID0gRkFMU0UpKSAlPiUgY29tcGFjdCgpCmBgYApgYGB7ciBtZXNzYWdlPUZBTFNFLCB3YXJuaW5nPUZBTFNFfQpkYXRhNC5QTFNyZXN1bHRzIDwtIHBscy5kYXRhNC5saXN0ICU+JSBtYXBfZGZyKH5nZXRfcGxvdGRhdGEoLikkbW9kZWxfc3RhdHMsIC5pZCA9ICJkYXRhc2V0IikKZGF0YTQuUExTcmVzdWx0cwoKZGF0YTQuY29tcGFyaXNvbiA8LSBmdWxsX2pvaW4oZGF0YTQuUENBcmVzdWx0cywgZGF0YTQuUExTcmVzdWx0cykgJT4lIGFkZF9jb2x1bW4oInBlcm1hbm92YSIgPSBkYXRhNC5wZXJtYW5vdmEpCgpwNC5wZXJtIDwtIGdncGxvdChkYXRhNC5jb21wYXJpc29uKSArCiAgZ2VvbV9kZW5zaXR5KGFlcyhQQzEucC52YWx1ZSksIGZpbGwgPSAiYmx1ZSIsIGFscGhhID0gMC4zMykgKwogIGdlb21fZGVuc2l0eShhZXMocFEyKSwgZmlsbCA9ICJyZWQiLCBhbHBoYSA9IDAuMzMpICsKICBnZW9tX2RlbnNpdHkoYWVzKHBlcm1hbm92YSksIGZpbGwgPSAiZ3JlZW4iLCBhbHBoYSA9IDAuMzMpICsKICBsYWJzKHggPSAicCB2YWx1ZSIpICsKICBnZW9tX3ZsaW5lKHhpbnRlcmNlcHQgPSAwLjA1LCBsaW5ldHlwZSA9IDUpICsKICBnZ3RpdGxlKCI0LiArIENvdmFyaWFuY2UsICsgRGlzY3JpbWluYXRpbmcgdmFyaWFibGVzIikKcDQucGVybQpgYGAKCiMgUGxvdCBleGFtcGxlIGRhdGFzZXRzCmBgYHtyfQpwbG90cyA8LSBwbG90X2dyaWQocDEscDIscDMscDQsIG5jb2wgPSAxLCBucm93ID0gNCkKc2F2ZV9wbG90KCJmaWdzL2ZpZzEucG5nIiwgcGxvdHMsIG5jb2wgPSAyLCBucm93ID0gNCwgYmFzZV9hc3BlY3RfcmF0aW8gPSAxLCBiYXNlX2hlaWdodCA9IDMuNSkKYGBgCgoKIyBQbG90IHBlcm11dGF0aW9uIHRlc3RpbmcgcmVzdWx0cwpgYGB7cn0KcGxvdHMucGVybSA8LSBwbG90X2dyaWQocDEucGVybSwgcDIucGVybSwgcDMucGVybSwgcDQucGVybSwgbmNvbCA9IDIsIG5yb3cgPSAyKQpwLnZhbF9maWd1cmUgPC0gcGxvdF9ncmlkKGdncGxvdCgpICsgZ2d0aXRsZSgiRGlzdHIuIG9mIHAgdmFsdWVzIGZyb20gdC10ZXN0cyBvbiBQQzEgKGJsdWUpLCBQTFMtREEgKHJlZCksIFBFUk1BTk9WQSAoZ3JlZW4pIiksIHBsb3RzLnBlcm0sIG5jb2wgPSAxLCByZWxfaGVpZ2h0cyA9IGMoMC4xLCAxKSkKcC52YWxfZmlndXJlCnNhdmVfcGxvdCgiZmlncy9maWcyLnBuZyIsIHAudmFsX2ZpZ3VyZSwgbmNvbCA9IDIsIG5yb3cgPSAyLCBiYXNlX2FzcGVjdF9yYXRpbyA9IDEuNSkKYGBgCgojIENvbmNsdXNpb25zCk5vbmUgb2YgdGhlIG1ldGhvZHMgZ2l2ZSBtb3JlIGZhbHNlIHBvc2l0aXZlcyB0aGFuIHRoZXkgc2hvdWxkLS0tdGhhdCBpcywgbm8gZGlmZmVyZW5jZXMgYXJlIGZvdW5kIHdoZW4gdGhlcmUgYXJlIG5vIGRpc2NyaW1pbmF0aW5nIHZhcmlhYmxlcy4KClBFUk1BTk9WQSBzZWVtcyB0byBwZXJmb3JtIHRoZSBiZXN0IGluIHRoZXNlIHNpbXVsYXRpb25zLCBmb2xsb3dlZCBieSBQTFMtREEuICBQQ0EgaXMgc2hpdCBhdCBmaW5kaW5nIHNlcGFyYXRpb24gd2hlbiBkaXNjcmltaW5hdGluZyB2YXJpYWJsZXMgYXJlIGEgc21hbGwgcG9ydGlvbiBvZiB0aGUgdG90YWwgdmFyaWFibGVzIG1lYXN1cmVkIChuZWVkbGUgaW4gYSBoYXlzdGFjayBzY2VuYXJpbykuCgpXaGVuIGNvdmFyaWFuY2UgaXMgYWRkZWQgdG8gdGhlICJoYXlzdGFjayIsIG9yIHRvIGJvdGggdGhlICJoYXlzdGFjayIgYW5kIHRoZSAibmVlZGxlIiwgdGhlIHBlcmZvcm1hbmNlIG9mIFBFUk1BTk9WQSBkcm9wcywgd2hpbGUgdGhlIHBlcmZvcm1hbmNlIG9mIFBMUy1EQSByZW1haW5zIGFib3V0IHRoZSBzYW1lLiBJIHNob3VsZCBwbGF5IGFyb3VuZCB3aXRoIHRoaXMgaW4gYSBzZXBhcmF0ZSBub3RlYm9vay4KCgojIFRyeWluZyBhbHRlcm5hdGUgbWV0aG9kIG9mIG1ha2luZyBkaXNjcmltaW5hdGluZyB2YXJpYWJsZXMKCkkgY2FuJ3QgZXZlbiBmaWd1cmUgb3V0IGhvdyB0byBtYWtlIFBMUy1EQSBmaW5kIGEgZGlmZmVyZW5jZS4KYGBge3J9CnNpbS5kYXRhNSA8LSBzaW0udmNvdihwX25vaXNlID0gMCwKICAgICAgICAgICAgICAgICAgICAgIHBfc2lnbmFsID0gMTUsCiAgICAgICAgICAgICAgICAgICAgICBwX2Rpc2MgPSAxNSwKICAgICAgICAgICAgICAgICAgICAgIGNvdl9zaWduYWwgPSAwLjgsCiAgICAgICAgICAgICAgICAgICAgICBjb3ZfZGlzYyA9IDAuNiwKICAgICAgICAgICAgICAgICAgICAgIGRpZmZfZGlzYyA9IDAsCiAgICAgICAgICAgICAgICAgICAgICB2YXIgPSAwLjgsCiAgICAgICAgICAgICAgICAgICAgICBOID0gMjAsCiAgICAgICAgICAgICAgICAgICAgICBzZWVkID0gTkEpCgpzaW0uZGF0YTUgPC0gc2ltLmRhdGE1ICU+JQogIHJlbmFtZShZID0gZGlzY18xKSAlPiUgCiAgYXJyYW5nZShZKSAlPiUgCiAgbXV0YXRlKGdyb3VwID0gYyhyZXAoImEiLCBucm93KC4pLzIpLCByZXAoImIiLCBucm93KC4pLzIpKSkKYGBgCgpgYGB7cn0KcGNhNSA8LSBvcGxzKHNlbGVjdChzaW0uZGF0YTUsIC1ZLCAtZ3JvdXApLCBwcmVkSSA9IDIpCnBjYV9wbG90KHBjYTUsIHNpbS5kYXRhNSRncm91cCkKYGBgCgpgYGB7cn0KcGxzZGE1IDwtIG9wbHMoc2VsZWN0KHNpbS5kYXRhNSwgLWdyb3VwLCAtWSksIHNpbS5kYXRhNSRncm91cCwgcGVybUkgPSAyMDAsIHByZWRJID0gMikKYGBgCmBgYHtyfQpnZXRfVklQKHBsc2RhNSkgJT4lIGFycmFuZ2UoZGVzYyhWSVApKQpgYGAKYGBge3J9CnBsczUgPC0gb3BscyhzZWxlY3Qoc2ltLmRhdGE1LCAtZ3JvdXAsIC1ZKSwgc2ltLmRhdGE1JFksIHBlcm1JID0gMjAwLCBwcmVkSSA9IDIpCmBgYAoKSHVoLCB0aGF0IGRvZXNuJ3Qgd29yawpgYGB7cn0KcGxvdGRhdGEgPC0gc2ltLmRhdGE1ICU+JSAKICBnYXRoZXIoLWdyb3VwLCAtWSwga2V5ID0gdmFyaWFibGUsIHZhbHVlID0gZGF0YSkgJT4lIAogIG11dGF0ZSh2YXJ0eXBlID0gY2FzZV93aGVuKHN0cl9kZXRlY3QodmFyaWFibGUsICJkaXNjIikgfiAiZGlzY3JpbWluYXRpbmciLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgIHN0cl9kZXRlY3QodmFyaWFibGUsICJub2lzZSIpIH4gIm5vIGNvdmFyaWFuY2UiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgIHN0cl9kZXRlY3QodmFyaWFibGUsICJzaWduYWwiKSB+ICJjb3ZhcnlpbmciKSkKZ2dwbG90KHBsb3RkYXRhLCBhZXMoeCA9IFksIHkgPSBkYXRhLCBjb2xvciA9IHZhcnR5cGUpKSArCiAgZ2VvbV9wb2ludCgpICsKICBnZW9tX3Ntb290aChtZXRob2QgPSJsbSIsIHNlID0gRkFMU0UpCmBgYAoqQWggaGEhIElzIGl0IGJlY2F1c2UgdGhlIGRpc2NyaW1pbmF0aW5nIGRhdGEgYWN0dWFsbHkgZG9lcyBOT1QgY28tdmFyeT8qCk5vLCBJIHVwcGVkIHRoZSBjb3ZhcmlhbmNlIGFuZCBpdCBkb2Vzbid0IGhlbHAuCgpQZXJtYW5vdmEKYGBge3J9CnBlcm1kYXRhNSA8LSBzaW0uZGF0YTUgJT4lCiAgIyBzZWxlY3QoLVkpICU+JSAKICBtdXRhdGVfaWYoaXMuZG91YmxlLCBzY2FsZSkKYWRvbmlzKHNlbGVjdChwZXJtZGF0YTUsIC1ncm91cCwgLVkpfnBlcm1kYXRhNSRncm91cCpwZXJtZGF0YTUkWSwgbWV0aG9kID0gImV1IikKYGBgCgo=